/* Xenbus code for blkif backend
Copyright (C) 2005 Rusty Russell <rusty@rustcorp.com.au>
+ Copyright (C) 2005 XenSource Ltd
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
+
#include <stdarg.h>
#include <linux/module.h>
#include <asm-xen/xenbus.h>
#include "common.h"
+
+#if 0
+#undef DPRINTK
+#define DPRINTK(fmt, args...) \
+ printk("blkback/xenbus (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args)
+#endif
+
+
struct backend_info
{
struct xenbus_device *dev;
-
- /* our communications channel */
blkif_t *blkif;
+ struct xenbus_watch backend_watch;
- long int frontend_id;
long int pdev;
long int readonly;
+};
- /* watch back end for changes */
- struct xenbus_watch backend_watch;
- /* watch front end for changes */
- struct xenbus_watch watch;
- char *frontpath;
-};
+static void maybe_connect(struct backend_info *);
+static void connect(struct backend_info *);
+static int connect_ring(struct backend_info *);
+static void backend_changed(struct xenbus_watch *, const char **,
+ unsigned int);
+
static int blkback_remove(struct xenbus_device *dev)
{
struct backend_info *be = dev->data;
- if (be->watch.node)
- unregister_xenbus_watch(&be->watch);
- unregister_xenbus_watch(&be->backend_watch);
- if (be->blkif)
+ DPRINTK("");
+
+ if (be->backend_watch.node) {
+ unregister_xenbus_watch(&be->backend_watch);
+ kfree(be->backend_watch.node);
+ be->backend_watch.node = NULL;
+ }
+ if (be->blkif) {
blkif_put(be->blkif);
- kfree(be->frontpath);
+ be->blkif = NULL;
+ }
kfree(be);
+ dev->data = NULL;
return 0;
}
-/* Front end tells us frame. */
-static void frontend_changed(struct xenbus_watch *watch,
- const char **vec, unsigned int len)
+
+/**
+ * Entry point to this code when a new device is created. Allocate the basic
+ * structures, and watch the store waiting for the hotplug scripts to tell us
+ * the device's physical-device. Switch to InitWait.
+ */
+static int blkback_probe(struct xenbus_device *dev,
+ const struct xenbus_device_id *id)
{
- unsigned long ring_ref;
- unsigned int evtchn;
int err;
- struct xenbus_transaction *xbt;
- struct backend_info *be
- = container_of(watch, struct backend_info, watch);
-
- /* If other end is gone, delete ourself. */
- if (vec && !xenbus_exists(NULL, be->frontpath, "")) {
- device_unregister(&be->dev->dev);
- return;
- }
- if (be->blkif == NULL || be->blkif->status == CONNECTED)
- return;
-
- err = xenbus_gather(NULL, be->frontpath, "ring-ref", "%lu", &ring_ref,
- "event-channel", "%u", &evtchn, NULL);
- if (err) {
- xenbus_dev_error(be->dev, err,
- "reading %s/ring-ref and event-channel",
- be->frontpath);
- return;
- }
-
- /* Map the shared frame, irq etc. */
- err = blkif_map(be->blkif, ring_ref, evtchn);
- if (err) {
- xenbus_dev_error(be->dev, err,
- "mapping ring-ref %lu port %u",
- ring_ref, evtchn);
- return;
- }
- /* XXX From here on should 'blkif_unmap' on error. */
-
-again:
- /* Supply the information about the device the frontend needs */
- xbt = xenbus_transaction_start();
- if (IS_ERR(xbt)) {
- xenbus_dev_error(be->dev, err, "starting transaction");
- return;
+ struct backend_info *be = kmalloc(sizeof(struct backend_info),
+ GFP_KERNEL);
+ if (!be) {
+ xenbus_dev_fatal(dev, -ENOMEM,
+ "allocating backend structure");
+ return -ENOMEM;
}
+ memset(be, 0, sizeof(*be));
- err = xenbus_printf(xbt, be->dev->nodename, "sectors", "%lu",
- vbd_size(&be->blkif->vbd));
- if (err) {
- xenbus_dev_error(be->dev, err, "writing %s/sectors",
- be->dev->nodename);
- goto abort;
- }
+ be->dev = dev;
+ dev->data = be;
- /* FIXME: use a typename instead */
- err = xenbus_printf(xbt, be->dev->nodename, "info", "%u",
- vbd_info(&be->blkif->vbd));
- if (err) {
- xenbus_dev_error(be->dev, err, "writing %s/info",
- be->dev->nodename);
- goto abort;
- }
- err = xenbus_printf(xbt, be->dev->nodename, "sector-size", "%lu",
- vbd_secsize(&be->blkif->vbd));
- if (err) {
- xenbus_dev_error(be->dev, err, "writing %s/sector-size",
- be->dev->nodename);
- goto abort;
+ be->blkif = alloc_blkif(dev->otherend_id);
+ if (IS_ERR(be->blkif)) {
+ err = PTR_ERR(be->blkif);
+ be->blkif = NULL;
+ xenbus_dev_fatal(dev, err, "creating block interface");
+ goto fail;
}
- err = xenbus_transaction_end(xbt, 0);
- if (err == -EAGAIN)
- goto again;
- if (err) {
- xenbus_dev_error(be->dev, err, "ending transaction",
- ring_ref, evtchn);
- goto abort;
- }
+ err = xenbus_watch_path2(dev, dev->nodename, "physical-device",
+ &be->backend_watch, backend_changed);
+ if (err)
+ goto fail;
- xenbus_dev_ok(be->dev);
+ err = xenbus_switch_state(dev, NULL, XenbusStateInitWait);
+ if (err)
+ goto fail;
- return;
+ return 0;
- abort:
- xenbus_transaction_end(xbt, 1);
+fail:
+ DPRINTK("failed");
+ blkback_remove(dev);
+ return err;
}
-/*
- Setup supplies physical device.
- We provide event channel and device details to front end.
- Frontend supplies shared frame and event channel.
+
+/**
+ * Callback received when the hotplug scripts have placed the physical-device
+ * node. Read it and the read-only node, and create a vbd. If the frontend
+ * is ready, connect.
*/
static void backend_changed(struct xenbus_watch *watch,
const char **vec, unsigned int len)
{
int err;
char *p;
- long int handle, pdev;
+ long pdev;
struct backend_info *be
= container_of(watch, struct backend_info, backend_watch);
struct xenbus_device *dev = be->dev;
+ DPRINTK("");
+
err = xenbus_scanf(NULL, dev->nodename,
"physical-device", "%li", &pdev);
- if (XENBUS_EXIST_ERR(err))
+ if (XENBUS_EXIST_ERR(err)) {
+ /* Since this watch will fire once immediately after it is
+ registered, we expect this. Ignore it, and wait for the
+ hotplug scripts. */
return;
- if (err < 0) {
- xenbus_dev_error(dev, err, "reading physical-device");
+ }
+ if (err != 1) {
+ xenbus_dev_fatal(dev, err, "reading physical-device");
return;
}
if (be->pdev && be->pdev != pdev) {
printk(KERN_WARNING
- "changing physical-device not supported\n");
+ "blkback: changing physical-device (from %ld to %ld) "
+ "not supported.\n", be->pdev, pdev);
return;
}
- be->pdev = pdev;
/* If there's a read-only node, we're read only. */
p = xenbus_read(NULL, dev->nodename, "read-only", NULL);
kfree(p);
}
- if (be->blkif == NULL) {
+ if (be->pdev == 0L) {
/* Front end dir is a number, which is used as the handle. */
- p = strrchr(be->frontpath, '/') + 1;
+
+ long handle;
+
+ p = strrchr(dev->otherend, '/') + 1;
handle = simple_strtoul(p, NULL, 0);
- be->blkif = alloc_blkif(be->frontend_id);
- if (IS_ERR(be->blkif)) {
- err = PTR_ERR(be->blkif);
- be->blkif = NULL;
- xenbus_dev_error(dev, err,
- "creating block interface");
- return;
- }
+ be->pdev = pdev;
err = vbd_create(be->blkif, handle, be->pdev, be->readonly);
if (err) {
- blkif_put(be->blkif);
- be->blkif = NULL;
- xenbus_dev_error(dev, err,
+ be->pdev = 0L;
+ xenbus_dev_fatal(dev, err,
"creating vbd structure");
return;
}
- /* Pass in NULL node to skip exist test. */
- frontend_changed(&be->watch, NULL, 0);
+ maybe_connect(be);
}
}
-static int blkback_probe(struct xenbus_device *dev,
- const struct xenbus_device_id *id)
+
+/**
+ * Callback received when the frontend's state changes.
+ */
+static void frontend_changed(struct xenbus_device *dev,
+ XenbusState frontend_state)
{
- struct backend_info *be;
- char *frontend;
+ struct backend_info *be = dev->data;
int err;
- be = kmalloc(sizeof(*be), GFP_KERNEL);
- if (!be) {
- xenbus_dev_error(dev, -ENOMEM,
- "allocating backend structure");
- return -ENOMEM;
+ DPRINTK("");
+
+ switch (frontend_state) {
+ case XenbusStateInitialising:
+ case XenbusStateConnected:
+ break;
+
+ case XenbusStateInitialised:
+ err = connect_ring(be);
+ if (err) {
+ return;
+ }
+ maybe_connect(be);
+ break;
+
+ case XenbusStateClosing:
+ xenbus_switch_state(dev, NULL, XenbusStateClosing);
+ break;
+
+ case XenbusStateClosed:
+ device_unregister(&dev->dev);
+ break;
+
+ case XenbusStateUnknown:
+ case XenbusStateInitWait:
+ default:
+ xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
+ frontend_state);
+ break;
}
- memset(be, 0, sizeof(*be));
+}
- frontend = NULL;
- err = xenbus_gather(NULL, dev->nodename,
- "frontend-id", "%li", &be->frontend_id,
- "frontend", NULL, &frontend,
- NULL);
- if (XENBUS_EXIST_ERR(err))
- goto free_be;
- if (err < 0) {
- xenbus_dev_error(dev, err,
- "reading %s/frontend or frontend-id",
- dev->nodename);
- goto free_be;
+
+/* ** Connection ** */
+
+
+static void maybe_connect(struct backend_info *be)
+{
+ if (be->pdev != 0L && be->blkif->status == CONNECTED) {
+ connect(be);
}
- if (strlen(frontend) == 0 || !xenbus_exists(NULL, frontend, "")) {
- /* If we can't get a frontend path and a frontend-id,
- * then our bus-id is no longer valid and we need to
- * destroy the backend device.
- */
- err = -ENOENT;
- goto free_be;
+}
+
+
+/**
+ * Write the physical details regarding the block device to the store, and
+ * switch to Connected state.
+ */
+static void connect(struct backend_info *be)
+{
+ struct xenbus_transaction *xbt;
+ int err;
+ struct xenbus_device *dev = be->dev;
+
+ DPRINTK("%s", dev->otherend);
+
+ /* Supply the information about the device the frontend needs */
+again:
+ xbt = xenbus_transaction_start();
+
+ if (IS_ERR(xbt)) {
+ err = PTR_ERR(xbt);
+ xenbus_dev_fatal(dev, err, "starting transaction");
+ return;
}
- be->dev = dev;
- be->backend_watch.node = dev->nodename;
- be->backend_watch.callback = backend_changed;
- /* Will implicitly call backend_changed once. */
- err = register_xenbus_watch(&be->backend_watch);
+ err = xenbus_printf(xbt, dev->nodename, "sectors", "%lu",
+ vbd_size(&be->blkif->vbd));
if (err) {
- be->backend_watch.node = NULL;
- xenbus_dev_error(dev, err,
- "adding backend watch on %s",
+ xenbus_dev_fatal(dev, err, "writing %s/sectors",
+ dev->nodename);
+ goto abort;
+ }
+
+ /* FIXME: use a typename instead */
+ err = xenbus_printf(xbt, dev->nodename, "info", "%u",
+ vbd_info(&be->blkif->vbd));
+ if (err) {
+ xenbus_dev_fatal(dev, err, "writing %s/info",
dev->nodename);
- goto free_be;
+ goto abort;
+ }
+ err = xenbus_printf(xbt, dev->nodename, "sector-size", "%lu",
+ vbd_secsize(&be->blkif->vbd));
+ if (err) {
+ xenbus_dev_fatal(dev, err, "writing %s/sector-size",
+ dev->nodename);
+ goto abort;
}
- be->frontpath = frontend;
- be->watch.node = be->frontpath;
- be->watch.callback = frontend_changed;
- err = register_xenbus_watch(&be->watch);
+ err = xenbus_switch_state(dev, xbt, XenbusStateConnected);
+ if (err)
+ goto abort;
+
+ err = xenbus_transaction_end(xbt, 0);
+ if (err == -EAGAIN)
+ goto again;
+ if (err)
+ xenbus_dev_fatal(dev, err, "ending transaction");
+ return;
+ abort:
+ xenbus_transaction_end(xbt, 1);
+}
+
+
+static int connect_ring(struct backend_info *be)
+{
+ struct xenbus_device *dev = be->dev;
+ unsigned long ring_ref;
+ unsigned int evtchn;
+ int err;
+
+ DPRINTK("%s", dev->otherend);
+
+ err = xenbus_gather(NULL, dev->otherend, "ring-ref", "%lu", &ring_ref,
+ "event-channel", "%u", &evtchn, NULL);
if (err) {
- be->watch.node = NULL;
- xenbus_dev_error(dev, err,
- "adding frontend watch on %s",
- be->frontpath);
- goto free_be;
+ xenbus_dev_fatal(dev, err,
+ "reading %s/ring-ref and event-channel",
+ dev->otherend);
+ return err;
}
- dev->data = be;
- return 0;
+ /* Map the shared frame, irq etc. */
+ err = blkif_map(be->blkif, ring_ref, evtchn);
+ if (err) {
+ xenbus_dev_fatal(dev, err, "mapping ring-ref %lu port %u",
+ ring_ref, evtchn);
+ return err;
+ }
- free_be:
- if (be->backend_watch.node)
- unregister_xenbus_watch(&be->backend_watch);
- kfree(frontend);
- kfree(be);
- return err;
+ return 0;
}
+
+/* ** Driver Registration ** */
+
+
static struct xenbus_device_id blkback_ids[] = {
{ "vbd" },
{ "" }
};
+
static struct xenbus_driver blkback = {
.name = "vbd",
.owner = THIS_MODULE,
.ids = blkback_ids,
.probe = blkback_probe,
.remove = blkback_remove,
+ .otherend_changed = frontend_changed
};
+
void blkif_xenbus_init(void)
{
xenbus_register_backend(&blkback);
}
+
/*
* Local variables:
* c-file-style: "linux"
* Copyright (c) 2004, Christian Limpach
* Copyright (c) 2004, Andrew Warfield
* Copyright (c) 2005, Christopher Clark
+ * Copyright (c) 2005, XenSource Ltd
*
* This file may be distributed separately from the Linux kernel, or
* incorporated into other software packages, subject to the following license:
* IN THE SOFTWARE.
*/
+
#if 1
#define ASSERT(p) \
if (!(p)) { printk("Assertion '%s' failed, line %d, file %s", #p , \
#define ASSERT(_p)
#endif
+
#include <linux/version.h>
#include "block.h"
#include <linux/cdrom.h>
#include <asm-xen/gnttab.h>
#include <asm/hypervisor.h>
+
#define BLKIF_STATE_DISCONNECTED 0
#define BLKIF_STATE_CONNECTED 1
(BLKIF_MAX_SEGMENTS_PER_REQUEST * BLKIF_RING_SIZE)
#define GRANT_INVALID_REF 0
-static void kick_pending_request_queues(struct blkfront_info *info);
-static void blkif_completion(struct blk_shadow *s);
+static void connect(struct blkfront_info *);
+static void blkfront_closing(struct xenbus_device *);
+static int blkfront_remove(struct xenbus_device *);
+static int talk_to_backend(struct xenbus_device *, struct blkfront_info *);
+static int setup_blkring(struct xenbus_device *, struct blkfront_info *);
+
+static void kick_pending_request_queues(struct blkfront_info *);
+
+static irqreturn_t blkif_int(int irq, void *dev_id, struct pt_regs *ptregs);
+static void blkif_restart_queue(void *arg);
+static void blkif_recover(struct blkfront_info *);
+static void blkif_completion(struct blk_shadow *);
+static void blkif_free(struct blkfront_info *);
+
+
+/**
+ * Entry point to this code when a new device is created. Allocate the basic
+ * structures and the ring buffer for communication with the backend, and
+ * inform the backend of the appropriate details for those. Switch to
+ * Initialised state.
+ */
+static int blkfront_probe(struct xenbus_device *dev,
+ const struct xenbus_device_id *id)
+{
+ int err, vdevice, i;
+ struct blkfront_info *info;
+
+ /* FIXME: Use dynamic device id if this is not set. */
+ err = xenbus_scanf(NULL, dev->nodename,
+ "virtual-device", "%i", &vdevice);
+ if (err != 1) {
+ xenbus_dev_fatal(dev, err, "reading virtual-device");
+ return err;
+ }
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
+ return -ENOMEM;
+ }
+ info->xbdev = dev;
+ info->vdevice = vdevice;
+ info->connected = BLKIF_STATE_DISCONNECTED;
+ info->mi = NULL;
+ info->gd = NULL;
+ INIT_WORK(&info->work, blkif_restart_queue, (void *)info);
+
+ info->shadow_free = 0;
+ memset(info->shadow, 0, sizeof(info->shadow));
+ for (i = 0; i < BLK_RING_SIZE; i++)
+ info->shadow[i].req.id = i+1;
+ info->shadow[BLK_RING_SIZE-1].req.id = 0x0fffffff;
+
+ /* Front end dir is a number, which is used as the id. */
+ info->handle = simple_strtoul(strrchr(dev->nodename,'/')+1, NULL, 0);
+ dev->data = info;
+
+ err = talk_to_backend(dev, info);
+ if (err) {
+ kfree(info);
+ dev->data = NULL;
+ return err;
+ }
+
+ return 0;
+}
+
+
+/**
+ * We are reconnecting to the backend, due to a suspend/resume, or a backend
+ * driver restart. We tear down our blkif structure and recreate it, but
+ * leave the device-layer structures intact so that this is transparent to the
+ * rest of the kernel.
+ */
+static int blkfront_resume(struct xenbus_device *dev)
+{
+ struct blkfront_info *info = dev->data;
+ int err;
+
+ DPRINTK("blkfront_resume: %s\n", dev->nodename);
+
+ blkif_free(info);
+
+ err = talk_to_backend(dev, info);
+ if (!err)
+ blkif_recover(info);
+
+ return err;
+}
+
+
+/* Common code used when first setting up, and when resuming. */
+static int talk_to_backend(struct xenbus_device *dev,
+ struct blkfront_info *info)
+{
+ const char *message = NULL;
+ struct xenbus_transaction *xbt;
+ int err;
+
+ /* Create shared ring, alloc event channel. */
+ err = setup_blkring(dev, info);
+ if (err)
+ goto out;
+
+again:
+ xbt = xenbus_transaction_start();
+ if (IS_ERR(xbt)) {
+ xenbus_dev_fatal(dev, err, "starting transaction");
+ goto destroy_blkring;
+ }
+
+ err = xenbus_printf(xbt, dev->nodename,
+ "ring-ref","%u", info->ring_ref);
+ if (err) {
+ message = "writing ring-ref";
+ goto abort_transaction;
+ }
+ err = xenbus_printf(xbt, dev->nodename,
+ "event-channel", "%u", info->evtchn);
+ if (err) {
+ message = "writing event-channel";
+ goto abort_transaction;
+ }
+
+ err = xenbus_switch_state(dev, xbt, XenbusStateInitialised);
+ if (err) {
+ goto abort_transaction;
+ }
+
+ err = xenbus_transaction_end(xbt, 0);
+ if (err) {
+ if (err == -EAGAIN)
+ goto again;
+ xenbus_dev_fatal(dev, err, "completing transaction");
+ goto destroy_blkring;
+ }
+
+ return 0;
+
+ abort_transaction:
+ xenbus_transaction_end(xbt, 1);
+ if (message)
+ xenbus_dev_fatal(dev, err, "%s", message);
+ destroy_blkring:
+ blkif_free(info);
+ out:
+ return err;
+}
+
+
+static int setup_blkring(struct xenbus_device *dev,
+ struct blkfront_info *info)
+{
+ blkif_sring_t *sring;
+ int err;
+
+ info->ring_ref = GRANT_INVALID_REF;
+
+ sring = (blkif_sring_t *)__get_free_page(GFP_KERNEL);
+ if (!sring) {
+ xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring");
+ return -ENOMEM;
+ }
+ SHARED_RING_INIT(sring);
+ FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
+
+ err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring));
+ if (err < 0) {
+ free_page((unsigned long)sring);
+ info->ring.sring = NULL;
+ goto fail;
+ }
+ info->ring_ref = err;
+
+ err = xenbus_alloc_evtchn(dev, &info->evtchn);
+ if (err)
+ goto fail;
+
+ err = bind_evtchn_to_irqhandler(
+ info->evtchn, blkif_int, SA_SAMPLE_RANDOM, "blkif", info);
+ if (err <= 0) {
+ xenbus_dev_fatal(dev, err,
+ "bind_evtchn_to_irqhandler failed");
+ goto fail;
+ }
+ info->irq = err;
+
+ return 0;
+fail:
+ blkif_free(info);
+ return err;
+}
+
+
+/**
+ * Callback received when the backend's state changes.
+ */
+static void backend_changed(struct xenbus_device *dev,
+ XenbusState backend_state)
+{
+ struct blkfront_info *info = dev->data;
+
+ DPRINTK("blkfront:backend_changed.\n");
+
+ switch (backend_state) {
+ case XenbusStateUnknown:
+ case XenbusStateInitialising:
+ case XenbusStateInitWait:
+ case XenbusStateInitialised:
+ case XenbusStateClosed:
+ break;
+
+ case XenbusStateConnected:
+ connect(info);
+ break;
+
+ case XenbusStateClosing:
+ blkfront_closing(dev);
+ break;
+ }
+}
+
+
+/* ** Connection ** */
+
+
+static void connect(struct blkfront_info *info)
+{
+ unsigned long sectors, sector_size;
+ unsigned int binfo;
+
+ if (info->connected == BLKIF_STATE_CONNECTED)
+ return;
+
+ DPRINTK("blkfront.c:connect:%s.\n", info->xbdev->otherend);
+
+ int err = xenbus_gather(NULL, info->xbdev->otherend,
+ "sectors", "%lu", §ors,
+ "info", "%u", &binfo,
+ "sector-size", "%lu", §or_size,
+ NULL);
+ if (err) {
+ xenbus_dev_fatal(info->xbdev, err,
+ "reading backend fields at %s",
+ info->xbdev->otherend);
+ return;
+ }
+
+ info->connected = BLKIF_STATE_CONNECTED;
+ xlvbd_add(sectors, info->vdevice, binfo, sector_size, info);
+
+ err = xenbus_switch_state(info->xbdev, NULL, XenbusStateConnected);
+ if (err)
+ return;
+
+ /* Kick pending requests. */
+ spin_lock_irq(&blkif_io_lock);
+ kick_pending_request_queues(info);
+ spin_unlock_irq(&blkif_io_lock);
+}
+
+
+/**
+ * Handle the change of state of the backend to Closing. We must delete our
+ * device-layer structures now, to ensure that writes are flushed through to
+ * the backend. Once is this done, we can switch to Closed in
+ * acknowledgement.
+ */
+static void blkfront_closing(struct xenbus_device *dev)
+{
+ struct blkfront_info *info = dev->data;
+
+ DPRINTK("blkfront_closing: %s removed\n", dev->nodename);
+
+ if (info->mi) {
+ DPRINTK("Calling xlvbd_del\n");
+ xlvbd_del(info);
+ info->mi = NULL;
+ }
+
+ xenbus_switch_state(dev, NULL, XenbusStateClosed);
+}
+
+
+static int blkfront_remove(struct xenbus_device *dev)
+{
+ DPRINTK("blkfront_remove: %s removed\n", dev->nodename);
+
+ struct blkfront_info *info = dev->data;
+
+ blkif_free(info);
+
+ kfree(info);
+
+ return 0;
+}
+
static inline int GET_ID_FROM_FREELIST(
struct blkfront_info *info)
gnttab_grant_foreign_access_ref(
ref,
- info->backend_id,
+ info->xbdev->otherend_id,
buffer_mfn,
rq_data_dir(req) );
if (info->irq)
unbind_from_irqhandler(info->irq, info);
info->evtchn = info->irq = 0;
+
+}
+
+static void blkif_completion(struct blk_shadow *s)
+{
+ int i;
+ for (i = 0; i < s->req.nr_segments; i++)
+ gnttab_end_foreign_access(
+ blkif_gref_from_fas(s->req.frame_and_sects[i]), 0, 0UL);
}
static void blkif_recover(struct blkfront_info *info)
for (j = 0; j < req->nr_segments; j++)
gnttab_grant_foreign_access_ref(
blkif_gref_from_fas(req->frame_and_sects[j]),
- info->backend_id,
+ info->xbdev->otherend_id,
pfn_to_mfn(info->shadow[req->id].frame[j]),
rq_data_dir(
(struct request *)
info->connected = BLKIF_STATE_CONNECTED;
}
-static void blkif_connect(struct blkfront_info *info, u16 evtchn)
-{
- int err = 0;
-
- info->evtchn = evtchn;
- err = bind_evtchn_to_irqhandler(
- info->evtchn, blkif_int, SA_SAMPLE_RANDOM, "blkif", info);
- if (err <= 0) {
- WPRINTK("bind_evtchn_to_irqhandler failed (err=%d)\n", err);
- return;
- }
-
- info->irq = err;
-}
+/* ** Driver Registration ** */
static struct xenbus_device_id blkfront_ids[] = {
{ "" }
};
-static void watch_for_status(struct xenbus_watch *watch,
- const char **vec, unsigned int len)
-{
- struct blkfront_info *info;
- unsigned int binfo;
- unsigned long sectors, sector_size;
- int err;
- const char *node;
-
- node = vec[XS_WATCH_PATH];
-
- info = container_of(watch, struct blkfront_info, watch);
- node += strlen(watch->node);
-
- /* FIXME: clean up when error on the other end. */
- if ((info->connected == BLKIF_STATE_CONNECTED) || info->mi)
- return;
-
- err = xenbus_gather(NULL, watch->node,
- "sectors", "%lu", §ors,
- "info", "%u", &binfo,
- "sector-size", "%lu", §or_size,
- NULL);
- if (err) {
- xenbus_dev_error(info->xbdev, err,
- "reading backend fields at %s", watch->node);
- return;
- }
-
- info->connected = BLKIF_STATE_CONNECTED;
- xlvbd_add(sectors, info->vdevice, binfo, sector_size, info);
-
- xenbus_dev_ok(info->xbdev);
-
- /* Kick pending requests. */
- spin_lock_irq(&blkif_io_lock);
- kick_pending_request_queues(info);
- spin_unlock_irq(&blkif_io_lock);
-}
-
-static int setup_blkring(struct xenbus_device *dev, struct blkfront_info *info)
-{
- blkif_sring_t *sring;
- int err;
- evtchn_op_t op = {
- .cmd = EVTCHNOP_alloc_unbound,
- .u.alloc_unbound.dom = DOMID_SELF,
- .u.alloc_unbound.remote_dom = info->backend_id };
-
- info->ring_ref = GRANT_INVALID_REF;
-
- sring = (void *)__get_free_page(GFP_KERNEL);
- if (!sring) {
- xenbus_dev_error(dev, -ENOMEM, "allocating shared ring");
- return -ENOMEM;
- }
- SHARED_RING_INIT(sring);
- FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
-
- err = gnttab_grant_foreign_access(info->backend_id,
- virt_to_mfn(info->ring.sring), 0);
- if (err == -ENOSPC) {
- free_page((unsigned long)info->ring.sring);
- info->ring.sring = 0;
- xenbus_dev_error(dev, err, "granting access to ring page");
- return err;
- }
- info->ring_ref = err;
-
- err = HYPERVISOR_event_channel_op(&op);
- if (err) {
- gnttab_end_foreign_access(info->ring_ref, 0,
- (unsigned long)info->ring.sring);
- info->ring_ref = GRANT_INVALID_REF;
- info->ring.sring = NULL;
- xenbus_dev_error(dev, err, "allocating event channel");
- return err;
- }
-
- blkif_connect(info, op.u.alloc_unbound.port);
-
- return 0;
-}
-
-/* Common code used when first setting up, and when resuming. */
-static int talk_to_backend(struct xenbus_device *dev,
- struct blkfront_info *info)
-{
- char *backend;
- const char *message;
- struct xenbus_transaction *xbt;
- int err;
-
- backend = NULL;
- err = xenbus_gather(NULL, dev->nodename,
- "backend-id", "%i", &info->backend_id,
- "backend", NULL, &backend,
- NULL);
- if (XENBUS_EXIST_ERR(err))
- goto out;
- if (backend && strlen(backend) == 0) {
- err = -ENOENT;
- goto out;
- }
- if (err < 0) {
- xenbus_dev_error(dev, err, "reading %s/backend or backend-id",
- dev->nodename);
- goto out;
- }
-
- /* Create shared ring, alloc event channel. */
- err = setup_blkring(dev, info);
- if (err) {
- xenbus_dev_error(dev, err, "setting up block ring");
- goto out;
- }
-
-again:
- xbt = xenbus_transaction_start();
- if (IS_ERR(xbt)) {
- xenbus_dev_error(dev, err, "starting transaction");
- goto destroy_blkring;
- }
-
- err = xenbus_printf(xbt, dev->nodename,
- "ring-ref","%u", info->ring_ref);
- if (err) {
- message = "writing ring-ref";
- goto abort_transaction;
- }
- err = xenbus_printf(xbt, dev->nodename,
- "event-channel", "%u", info->evtchn);
- if (err) {
- message = "writing event-channel";
- goto abort_transaction;
- }
-
- err = xenbus_transaction_end(xbt, 0);
- if (err) {
- if (err == -EAGAIN)
- goto again;
- xenbus_dev_error(dev, err, "completing transaction");
- goto destroy_blkring;
- }
-
- info->watch.node = backend;
- info->watch.callback = watch_for_status;
- err = register_xenbus_watch(&info->watch);
- if (err) {
- message = "registering watch on backend";
- goto destroy_blkring;
- }
-
- info->backend = backend;
-
- return 0;
-
- abort_transaction:
- xenbus_transaction_end(xbt, 1);
- xenbus_dev_error(dev, err, "%s", message);
- destroy_blkring:
- blkif_free(info);
- out:
- kfree(backend);
- return err;
-}
-
-/* Setup supplies the backend dir, virtual device.
-
- We place an event channel and shared frame entries.
- We watch backend to wait if it's ok. */
-static int blkfront_probe(struct xenbus_device *dev,
- const struct xenbus_device_id *id)
-{
- int err, vdevice, i;
- struct blkfront_info *info;
-
- /* FIXME: Use dynamic device id if this is not set. */
- err = xenbus_scanf(NULL, dev->nodename,
- "virtual-device", "%i", &vdevice);
- if (XENBUS_EXIST_ERR(err))
- return err;
- if (err < 0) {
- xenbus_dev_error(dev, err, "reading virtual-device");
- return err;
- }
-
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (!info) {
- xenbus_dev_error(dev, err, "allocating info structure");
- return err;
- }
- info->xbdev = dev;
- info->vdevice = vdevice;
- info->connected = BLKIF_STATE_DISCONNECTED;
- info->mi = NULL;
- info->gd = NULL;
- INIT_WORK(&info->work, blkif_restart_queue, (void *)info);
-
- info->shadow_free = 0;
- memset(info->shadow, 0, sizeof(info->shadow));
- for (i = 0; i < BLK_RING_SIZE; i++)
- info->shadow[i].req.id = i+1;
- info->shadow[BLK_RING_SIZE-1].req.id = 0x0fffffff;
-
- /* Front end dir is a number, which is used as the id. */
- info->handle = simple_strtoul(strrchr(dev->nodename,'/')+1, NULL, 0);
- dev->data = info;
-
- err = talk_to_backend(dev, info);
- if (err) {
- kfree(info);
- dev->data = NULL;
- return err;
- }
-
- {
- unsigned int len = max(XS_WATCH_PATH, XS_WATCH_TOKEN) + 1;
- const char *vec[len];
-
- vec[XS_WATCH_PATH] = info->watch.node;
- vec[XS_WATCH_TOKEN] = NULL;
-
- /* Call once in case entries already there. */
- watch_for_status(&info->watch, vec, len);
- }
-
- return 0;
-}
-
-static int blkfront_remove(struct xenbus_device *dev)
-{
- struct blkfront_info *info = dev->data;
-
- if (info->backend)
- unregister_xenbus_watch(&info->watch);
-
- if (info->mi)
- xlvbd_del(info);
-
- blkif_free(info);
-
- kfree(info->backend);
- kfree(info);
-
- return 0;
-}
-
-static int blkfront_suspend(struct xenbus_device *dev)
-{
- struct blkfront_info *info = dev->data;
-
- unregister_xenbus_watch(&info->watch);
- kfree(info->backend);
- info->backend = NULL;
-
- return 0;
-}
-
-static int blkfront_resume(struct xenbus_device *dev)
-{
- struct blkfront_info *info = dev->data;
- int err;
-
- blkif_free(info);
-
- err = talk_to_backend(dev, info);
- if (!err)
- blkif_recover(info);
-
- return err;
-}
static struct xenbus_driver blkfront = {
.name = "vbd",
.probe = blkfront_probe,
.remove = blkfront_remove,
.resume = blkfront_resume,
- .suspend = blkfront_suspend,
+ .otherend_changed = backend_changed,
};
+
static int __init xlblk_init(void)
{
if (xen_init() < 0)
return -ENODEV;
- xenbus_register_driver(&blkfront);
- return 0;
+ return xenbus_register_frontend(&blkfront);
}
-
module_init(xlblk_init);
-static void blkif_completion(struct blk_shadow *s)
+
+static void xlblk_exit(void)
{
- int i;
- for (i = 0; i < s->req.nr_segments; i++)
- gnttab_end_foreign_access(
- blkif_gref_from_fas(s->req.frame_and_sects[i]), 0, 0UL);
+ return xenbus_unregister_driver(&blkfront);
}
+module_exit(xlblk_exit);
+
+MODULE_LICENSE("BSD");
+
/*
* Local variables:
struct blkfront_info
{
struct xenbus_device *xbdev;
- /* We watch the backend */
- struct xenbus_watch watch;
dev_t dev;
struct gendisk *gd;
int vdevice;
blkif_vdev_t handle;
int connected;
- char *backend;
- int backend_id;
int ring_ref;
blkif_front_ring_t ring;
unsigned int evtchn, irq;
/* Xenbus code for netif backend
Copyright (C) 2005 Rusty Russell <rusty@rustcorp.com.au>
+ Copyright (C) 2005 XenSource Ltd
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
+
#include <stdarg.h>
#include <linux/module.h>
#include <asm-xen/xenbus.h>
+#include <asm-xen/net_driver_util.h>
#include "common.h"
+
+#if 0
+#undef DPRINTK
+#define DPRINTK(fmt, args...) \
+ printk("netback/xenbus (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args)
+#endif
+
+
struct backend_info
{
struct xenbus_device *dev;
-
- /* our communications channel */
netif_t *netif;
+ struct xenbus_watch backend_watch;
+ XenbusState frontend_state;
+};
- long int frontend_id;
- /* watch back end for changes */
- struct xenbus_watch backend_watch;
+static int connect_rings(struct backend_info *);
+static void connect(struct backend_info *);
+static void maybe_connect(struct backend_info *);
+static void backend_changed(struct xenbus_watch *, const char **,
+ unsigned int);
- /* watch front end for changes */
- struct xenbus_watch watch;
- char *frontpath;
-};
static int netback_remove(struct xenbus_device *dev)
{
struct backend_info *be = dev->data;
- if (be->watch.node)
- unregister_xenbus_watch(&be->watch);
- unregister_xenbus_watch(&be->backend_watch);
- if (be->netif)
+ if (be->backend_watch.node) {
+ unregister_xenbus_watch(&be->backend_watch);
+ kfree(be->backend_watch.node);
+ be->backend_watch.node = NULL;
+ }
+ if (be->netif) {
netif_disconnect(be->netif);
- kfree(be->frontpath);
+ be->netif = NULL;
+ }
kfree(be);
+ dev->data = NULL;
return 0;
}
-/* Front end tells us frame. */
-static void frontend_changed(struct xenbus_watch *watch,
- const char **vec, unsigned int len)
+
+/**
+ * Entry point to this code when a new device is created. Allocate the basic
+ * structures, and watch the store waiting for the hotplug scripts to tell us
+ * the device's handle. Switch to InitWait.
+ */
+static int netback_probe(struct xenbus_device *dev,
+ const struct xenbus_device_id *id)
{
- unsigned long tx_ring_ref, rx_ring_ref;
- unsigned int evtchn;
int err;
- struct backend_info *be
- = container_of(watch, struct backend_info, watch);
- char *mac, *e, *s;
- int i;
-
- /* If other end is gone, delete ourself. */
- if (vec && !xenbus_exists(NULL, be->frontpath, "")) {
- xenbus_rm(NULL, be->dev->nodename, "");
- device_unregister(&be->dev->dev);
- return;
+ struct backend_info *be = kmalloc(sizeof(struct backend_info),
+ GFP_KERNEL);
+ if (!be) {
+ xenbus_dev_fatal(dev, -ENOMEM,
+ "allocating backend structure");
+ return -ENOMEM;
}
- if (be->netif == NULL || be->netif->status == CONNECTED)
- return;
+ memset(be, 0, sizeof(*be));
- mac = xenbus_read(NULL, be->frontpath, "mac", NULL);
- if (IS_ERR(mac)) {
- err = PTR_ERR(mac);
- xenbus_dev_error(be->dev, err, "reading %s/mac",
- be->dev->nodename);
- return;
- }
- s = mac;
- for (i = 0; i < ETH_ALEN; i++) {
- be->netif->fe_dev_addr[i] = simple_strtoul(s, &e, 16);
- if (s == e || (e[0] != ':' && e[0] != 0)) {
- kfree(mac);
- err = -ENOENT;
- xenbus_dev_error(be->dev, err, "parsing %s/mac",
- be->dev->nodename);
- return;
- }
- s = &e[1];
- }
- kfree(mac);
+ be->dev = dev;
+ dev->data = be;
- err = xenbus_gather(NULL, be->frontpath,
- "tx-ring-ref", "%lu", &tx_ring_ref,
- "rx-ring-ref", "%lu", &rx_ring_ref,
- "event-channel", "%u", &evtchn, NULL);
+ err = xenbus_watch_path2(dev, dev->nodename, "handle",
+ &be->backend_watch, backend_changed);
+ if (err)
+ goto fail;
+
+ err = xenbus_switch_state(dev, NULL, XenbusStateInitWait);
if (err) {
- xenbus_dev_error(be->dev, err,
- "reading %s/ring-ref and event-channel",
- be->frontpath);
- return;
+ goto fail;
}
- /* Map the shared frame, irq etc. */
- err = netif_map(be->netif, tx_ring_ref, rx_ring_ref, evtchn);
- if (err) {
- xenbus_dev_error(be->dev, err,
- "mapping shared-frames %lu/%lu port %u",
- tx_ring_ref, rx_ring_ref, evtchn);
- return;
+ return 0;
+
+fail:
+ DPRINTK("failed");
+ netback_remove(dev);
+ return err;
+}
+
+
+/**
+ * Handle the creation of the hotplug script environment. We add the script
+ * and vif variables to the environment, for the benefit of the vif-* hotplug
+ * scripts.
+ */
+static int netback_hotplug(struct xenbus_device *xdev, char **envp,
+ int num_envp, char *buffer, int buffer_size)
+{
+ struct backend_info *be = xdev->data;
+ netif_t *netif = be->netif;
+ int i = 0, length = 0;
+
+ DPRINTK("netback_hotplug");
+
+ char *val = xenbus_read(NULL, xdev->nodename, "script", NULL);
+ if (IS_ERR(val)) {
+ int err = PTR_ERR(val);
+ xenbus_dev_fatal(xdev, err, "reading script");
+ return err;
+ }
+ else {
+ add_hotplug_env_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "script=%s", val);
+ kfree(val);
}
- xenbus_dev_ok(be->dev);
+ add_hotplug_env_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "vif=%s", netif->dev->name);
+
+ envp[i] = NULL;
- return;
+ return 0;
}
-/*
- Setup supplies physical device.
- We provide event channel and device details to front end.
- Frontend supplies shared frame and event channel.
+
+/**
+ * Callback received when the hotplug scripts have placed the handle node.
+ * Read it, and create a netif structure. If the frontend is ready, connect.
*/
static void backend_changed(struct xenbus_watch *watch,
const char **vec, unsigned int len)
{
int err;
- long int handle;
+ long handle;
struct backend_info *be
= container_of(watch, struct backend_info, backend_watch);
struct xenbus_device *dev = be->dev;
- u8 be_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+
+ DPRINTK("");
err = xenbus_scanf(NULL, dev->nodename, "handle", "%li", &handle);
- if (XENBUS_EXIST_ERR(err))
+ if (XENBUS_EXIST_ERR(err)) {
+ /* Since this watch will fire once immediately after it is
+ registered, we expect this. Ignore it, and wait for the
+ hotplug scripts. */
return;
- if (err < 0) {
- xenbus_dev_error(dev, err, "reading handle");
+ }
+ if (err != 1) {
+ xenbus_dev_fatal(dev, err, "reading handle");
return;
}
if (be->netif == NULL) {
- be->netif = alloc_netif(be->frontend_id, handle, be_mac);
+ u8 be_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+
+ be->netif = alloc_netif(dev->otherend_id, handle, be_mac);
if (IS_ERR(be->netif)) {
err = PTR_ERR(be->netif);
be->netif = NULL;
- xenbus_dev_error(dev, err, "creating interface");
+ xenbus_dev_fatal(dev, err, "creating interface");
return;
}
kobject_hotplug(&dev->dev.kobj, KOBJ_ONLINE);
- /* Pass in NULL node to skip exist test. */
- frontend_changed(&be->watch, NULL, 0);
+ maybe_connect(be);
}
}
-static int netback_hotplug(struct xenbus_device *xdev, char **envp,
- int num_envp, char *buffer, int buffer_size)
+
+/**
+ * Callback received when the frontend's state changes.
+ */
+static void frontend_changed(struct xenbus_device *dev,
+ XenbusState frontend_state)
{
- struct backend_info *be = xdev->data;
- netif_t *netif = be->netif;
- int i = 0, length = 0;
+ struct backend_info *be = dev->data;
- char *val = xenbus_read(NULL, xdev->nodename, "script", NULL);
- if (IS_ERR(val)) {
- int err = PTR_ERR(val);
- xenbus_dev_error(xdev, err, "reading script");
- return err;
- }
- else {
- add_hotplug_env_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "script=%s", val);
- kfree(val);
+ DPRINTK("");
+
+ be->frontend_state = frontend_state;
+
+ switch (frontend_state) {
+ case XenbusStateInitialising:
+ case XenbusStateInitialised:
+ break;
+
+ case XenbusStateConnected:
+ maybe_connect(be);
+ break;
+
+ case XenbusStateClosing:
+ xenbus_switch_state(dev, NULL, XenbusStateClosing);
+ break;
+
+ case XenbusStateClosed:
+ device_unregister(&be->dev->dev);
+ break;
+
+ case XenbusStateUnknown:
+ case XenbusStateInitWait:
+ default:
+ xenbus_dev_fatal(be->dev, -EINVAL, "saw state %d at frontend",
+ frontend_state);
+ break;
}
+}
- add_hotplug_env_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "vif=%s", netif->dev->name);
- envp[i] = NULL;
+/* ** Connection ** */
- return 0;
+
+static void maybe_connect(struct backend_info *be)
+{
+ if (be->netif != NULL && be->frontend_state == XenbusStateConnected) {
+ connect(be);
+ }
}
-static int netback_probe(struct xenbus_device *dev,
- const struct xenbus_device_id *id)
+
+static void connect(struct backend_info *be)
{
- struct backend_info *be;
- char *frontend;
int err;
+ struct xenbus_device *dev = be->dev;
- be = kmalloc(sizeof(*be), GFP_KERNEL);
- if (!be) {
- xenbus_dev_error(dev, -ENOMEM, "allocating backend structure");
- return -ENOMEM;
- }
- memset(be, 0, sizeof(*be));
+ err = connect_rings(be);
+ if (err)
+ return;
- frontend = NULL;
- err = xenbus_gather(NULL, dev->nodename,
- "frontend-id", "%li", &be->frontend_id,
- "frontend", NULL, &frontend,
- NULL);
- if (XENBUS_EXIST_ERR(err))
- goto free_be;
- if (err < 0) {
- xenbus_dev_error(dev, err,
- "reading %s/frontend or frontend-id",
- dev->nodename);
- goto free_be;
- }
- if (strlen(frontend) == 0 || !xenbus_exists(NULL, frontend, "")) {
- /* If we can't get a frontend path and a frontend-id,
- * then our bus-id is no longer valid and we need to
- * destroy the backend device.
- */
- err = -ENOENT;
- goto free_be;
+ err = xen_net_read_mac(dev, be->netif->fe_dev_addr);
+ if (err) {
+ xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename);
+ return;
}
- be->dev = dev;
- be->backend_watch.node = dev->nodename;
- be->backend_watch.callback = backend_changed;
- /* Registration implicitly calls backend_changed. */
- err = register_xenbus_watch(&be->backend_watch);
+ xenbus_switch_state(dev, NULL, XenbusStateConnected);
+}
+
+
+static int connect_rings(struct backend_info *be)
+{
+ struct xenbus_device *dev = be->dev;
+ unsigned long tx_ring_ref, rx_ring_ref;
+ unsigned int evtchn;
+ int err;
+
+ DPRINTK("");
+
+ err = xenbus_gather(NULL, dev->otherend,
+ "tx-ring-ref", "%lu", &tx_ring_ref,
+ "rx-ring-ref", "%lu", &rx_ring_ref,
+ "event-channel", "%u", &evtchn, NULL);
if (err) {
- be->backend_watch.node = NULL;
- xenbus_dev_error(dev, err, "adding backend watch on %s",
- dev->nodename);
- goto free_be;
+ xenbus_dev_fatal(dev, err,
+ "reading %s/ring-ref and event-channel",
+ dev->otherend);
+ return err;
}
- be->frontpath = frontend;
- be->watch.node = be->frontpath;
- be->watch.callback = frontend_changed;
- err = register_xenbus_watch(&be->watch);
+ /* Map the shared frame, irq etc. */
+ err = netif_map(be->netif, tx_ring_ref, rx_ring_ref, evtchn);
if (err) {
- be->watch.node = NULL;
- xenbus_dev_error(dev, err,
- "adding frontend watch on %s",
- be->frontpath);
- goto free_be;
+ xenbus_dev_fatal(dev, err,
+ "mapping shared-frames %lu/%lu port %u",
+ tx_ring_ref, rx_ring_ref, evtchn);
+ return err;
}
-
- dev->data = be;
return 0;
-
- free_be:
- if (be->backend_watch.node)
- unregister_xenbus_watch(&be->backend_watch);
- kfree(frontend);
- kfree(be);
- return err;
}
+
+/* ** Driver Registration ** */
+
+
static struct xenbus_device_id netback_ids[] = {
{ "vif" },
{ "" }
};
+
static struct xenbus_driver netback = {
.name = "vif",
.owner = THIS_MODULE,
.probe = netback_probe,
.remove = netback_remove,
.hotplug = netback_hotplug,
+ .otherend_changed = frontend_changed,
};
+
void netif_xenbus_init(void)
{
xenbus_register_backend(&netback);
}
+
/*
* Local variables:
* c-file-style: "linux"
* Virtual network driver for conversing with remote driver backends.
*
* Copyright (c) 2002-2005, K A Fraser
+ * Copyright (c) 2005, XenSource Ltd
*
* This file may be distributed separately from the Linux kernel, or
* incorporated into other software packages, subject to the following license:
#include <asm/uaccess.h>
#include <asm-xen/xen-public/grant_table.h>
#include <asm-xen/gnttab.h>
+#include <asm-xen/net_driver_util.h>
#define GRANT_INVALID_REF 0
#define TX_TEST_IDX req_cons /* conservative: not seen all our requests? */
#endif
-
-static void network_tx_buf_gc(struct net_device *dev);
-static void network_alloc_rx_buffers(struct net_device *dev);
-
static unsigned long rx_pfn_array[NETIF_RX_RING_SIZE];
static multicall_entry_t rx_mcl[NETIF_RX_RING_SIZE+1];
static mmu_update_t rx_mmu[NETIF_RX_RING_SIZE];
-#ifdef CONFIG_PROC_FS
-static int xennet_proc_init(void);
-static int xennet_proc_addif(struct net_device *dev);
-static void xennet_proc_delif(struct net_device *dev);
-#else
-#define xennet_proc_init() (0)
-#define xennet_proc_addif(d) (0)
-#define xennet_proc_delif(d) ((void)0)
-#endif
-
-#define netfront_info net_private
-struct net_private
+struct netfront_info
{
struct list_head list;
struct net_device *netdev;
grant_ref_t grant_rx_ref[NETIF_TX_RING_SIZE + 1];
struct xenbus_device *xbdev;
- char *backend;
- int backend_id;
- struct xenbus_watch watch;
int tx_ring_ref;
int rx_ring_ref;
u8 mac[ETH_ALEN];
#ifdef DEBUG
#define DPRINTK(fmt, args...) \
- printk(KERN_ALERT "xen_net (%s:%d) " fmt, __FUNCTION__, __LINE__, ##args)
+ printk(KERN_ALERT "netfront (%s:%d) " fmt, __FUNCTION__, __LINE__, ##args)
#else
#define DPRINTK(fmt, args...) ((void)0)
#endif
#define IPRINTK(fmt, args...) \
- printk(KERN_INFO "xen_net: " fmt, ##args)
+ printk(KERN_INFO "netfront: " fmt, ##args)
#define WPRINTK(fmt, args...) \
- printk(KERN_WARNING "xen_net: " fmt, ##args)
+ printk(KERN_WARNING "netfront: " fmt, ##args)
+
+
+static int talk_to_backend(struct xenbus_device *, struct netfront_info *);
+static int setup_device(struct xenbus_device *, struct netfront_info *);
+static int create_netdev(int, struct xenbus_device *, struct net_device **);
+
+static void netfront_closing(struct xenbus_device *);
+
+static void end_access(int, void *);
+static void netif_disconnect_backend(struct netfront_info *);
+static void close_netdev(struct netfront_info *);
+static void netif_free(struct netfront_info *);
+
+static void show_device(struct netfront_info *);
+
+static void network_connect(struct net_device *);
+static void network_tx_buf_gc(struct net_device *);
+static void network_alloc_rx_buffers(struct net_device *);
+static int send_fake_arp(struct net_device *);
+
+static irqreturn_t netif_int(int irq, void *dev_id, struct pt_regs *ptregs);
+
+#ifdef CONFIG_PROC_FS
+static int xennet_proc_init(void);
+static int xennet_proc_addif(struct net_device *dev);
+static void xennet_proc_delif(struct net_device *dev);
+#else
+#define xennet_proc_init() (0)
+#define xennet_proc_addif(d) (0)
+#define xennet_proc_delif(d) ((void)0)
+#endif
+
+
+/**
+ * Entry point to this code when a new device is created. Allocate the basic
+ * structures and the ring buffers for communication with the backend, and
+ * inform the backend of the appropriate details for those. Switch to
+ * Connected state.
+ */
+static int netfront_probe(struct xenbus_device *dev,
+ const struct xenbus_device_id *id)
+{
+ int err;
+ struct net_device *netdev;
+ struct netfront_info *info;
+ unsigned int handle;
+
+ err = xenbus_scanf(NULL, dev->nodename, "handle", "%u", &handle);
+ if (err != 1) {
+ xenbus_dev_fatal(dev, err, "reading handle");
+ return err;
+ }
+
+ err = create_netdev(handle, dev, &netdev);
+ if (err) {
+ xenbus_dev_fatal(dev, err, "creating netdev");
+ return err;
+ }
+
+ info = netdev_priv(netdev);
+ dev->data = info;
+
+ err = talk_to_backend(dev, info);
+ if (err) {
+ kfree(info);
+ dev->data = NULL;
+ return err;
+ }
+
+ return 0;
+}
+
+
+/**
+ * We are reconnecting to the backend, due to a suspend/resume, or a backend
+ * driver restart. We tear down our netif structure and recreate it, but
+ * leave the device-layer structures intact so that this is transparent to the
+ * rest of the kernel.
+ */
+static int netfront_resume(struct xenbus_device *dev)
+{
+ struct netfront_info *info = dev->data;
+
+ DPRINTK("%s\n", dev->nodename);
+
+ netif_disconnect_backend(info);
+ return talk_to_backend(dev, info);
+}
+
+
+/* Common code used when first setting up, and when resuming. */
+static int talk_to_backend(struct xenbus_device *dev,
+ struct netfront_info *info)
+{
+ const char *message;
+ struct xenbus_transaction *xbt;
+ int err;
+
+ err = xen_net_read_mac(dev, info->mac);
+ if (err) {
+ xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename);
+ goto out;
+ }
+
+ /* Create shared ring, alloc event channel. */
+ err = setup_device(dev, info);
+ if (err)
+ goto out;
+
+again:
+ xbt = xenbus_transaction_start();
+ if (IS_ERR(xbt)) {
+ xenbus_dev_fatal(dev, err, "starting transaction");
+ goto destroy_ring;
+ }
+
+ err = xenbus_printf(xbt, dev->nodename, "tx-ring-ref","%u",
+ info->tx_ring_ref);
+ if (err) {
+ message = "writing tx ring-ref";
+ goto abort_transaction;
+ }
+ err = xenbus_printf(xbt, dev->nodename, "rx-ring-ref","%u",
+ info->rx_ring_ref);
+ if (err) {
+ message = "writing rx ring-ref";
+ goto abort_transaction;
+ }
+ err = xenbus_printf(xbt, dev->nodename,
+ "event-channel", "%u", info->evtchn);
+ if (err) {
+ message = "writing event-channel";
+ goto abort_transaction;
+ }
+
+ err = xenbus_printf(xbt, dev->nodename,
+ "state", "%d", XenbusStateConnected);
+ if (err) {
+ message = "writing frontend XenbusStateConnected";
+ goto abort_transaction;
+ }
+
+ err = xenbus_transaction_end(xbt, 0);
+ if (err) {
+ if (err == -EAGAIN)
+ goto again;
+ xenbus_dev_fatal(dev, err, "completing transaction");
+ goto destroy_ring;
+ }
+
+ return 0;
+
+ abort_transaction:
+ xenbus_transaction_end(xbt, 1);
+ xenbus_dev_fatal(dev, err, "%s", message);
+ destroy_ring:
+ netif_free(info);
+ out:
+ return err;
+}
+
+
+static int setup_device(struct xenbus_device *dev, struct netfront_info *info)
+{
+ int err;
+ struct net_device *netdev = info->netdev;
+
+ info->tx_ring_ref = GRANT_INVALID_REF;
+ info->rx_ring_ref = GRANT_INVALID_REF;
+ info->rx = NULL;
+ info->tx = NULL;
+ info->irq = 0;
+
+ info->tx = (netif_tx_interface_t *)__get_free_page(GFP_KERNEL);
+ if (!info->tx) {
+ err = -ENOMEM;
+ xenbus_dev_fatal(dev, err, "allocating tx ring page");
+ goto fail;
+ }
+ info->rx = (netif_rx_interface_t *)__get_free_page(GFP_KERNEL);
+ if (!info->rx) {
+ err = -ENOMEM;
+ xenbus_dev_fatal(dev, err, "allocating rx ring page");
+ goto fail;
+ }
+ memset(info->tx, 0, PAGE_SIZE);
+ memset(info->rx, 0, PAGE_SIZE);
+ info->backend_state = BEST_DISCONNECTED;
+
+ err = xenbus_grant_ring(dev, virt_to_mfn(info->tx));
+ if (err < 0)
+ goto fail;
+ info->tx_ring_ref = err;
+
+ err = xenbus_grant_ring(dev, virt_to_mfn(info->rx));
+ if (err < 0)
+ goto fail;
+ info->rx_ring_ref = err;
+
+ err = xenbus_alloc_evtchn(dev, &info->evtchn);
+ if (err)
+ goto fail;
+
+ memcpy(netdev->dev_addr, info->mac, ETH_ALEN);
+ network_connect(netdev);
+ info->irq = bind_evtchn_to_irqhandler(
+ info->evtchn, netif_int, SA_SAMPLE_RANDOM, netdev->name,
+ netdev);
+ (void)send_fake_arp(netdev);
+ show_device(info);
+
+ return 0;
+
+ fail:
+ netif_free(info);
+ return err;
+}
+
+
+/**
+ * Callback received when the backend's state changes.
+ */
+static void backend_changed(struct xenbus_device *dev,
+ XenbusState backend_state)
+{
+ DPRINTK("\n");
+
+ switch (backend_state) {
+ case XenbusStateInitialising:
+ case XenbusStateInitWait:
+ case XenbusStateInitialised:
+ case XenbusStateConnected:
+ case XenbusStateUnknown:
+ case XenbusStateClosed:
+ break;
+
+ case XenbusStateClosing:
+ netfront_closing(dev);
+ break;
+ }
+}
-static void netif_free(struct netfront_info *info);
/** Send a packet on a net device to encourage switches to learn the
* MAC. We send a fake ARP request.
return dev_queue_xmit(skb);
}
+
static int network_open(struct net_device *dev)
{
- struct net_private *np = netdev_priv(dev);
+ struct netfront_info *np = netdev_priv(dev);
memset(&np->stats, 0, sizeof(np->stats));
{
NETIF_RING_IDX i, prod;
unsigned short id;
- struct net_private *np = netdev_priv(dev);
+ struct netfront_info *np = netdev_priv(dev);
struct sk_buff *skb;
if (np->backend_state != BEST_CONNECTED)
static void network_alloc_rx_buffers(struct net_device *dev)
{
unsigned short id;
- struct net_private *np = netdev_priv(dev);
+ struct netfront_info *np = netdev_priv(dev);
struct sk_buff *skb;
int i, batch_target;
NETIF_RING_IDX req_prod = np->rx->req_prod;
ref = gnttab_claim_grant_reference(&np->gref_rx_head);
BUG_ON((signed short)ref < 0);
np->grant_rx_ref[id] = ref;
- gnttab_grant_foreign_transfer_ref(ref, np->backend_id);
+ gnttab_grant_foreign_transfer_ref(ref,
+ np->xbdev->otherend_id);
np->rx->ring[MASK_NETIF_RX_IDX(req_prod + i)].req.gref = ref;
rx_pfn_array[i] = virt_to_mfn(skb->head);
static int network_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
unsigned short id;
- struct net_private *np = netdev_priv(dev);
+ struct netfront_info *np = netdev_priv(dev);
netif_tx_request_t *tx;
NETIF_RING_IDX i;
grant_ref_t ref;
BUG_ON((signed short)ref < 0);
mfn = virt_to_mfn(skb->data);
gnttab_grant_foreign_access_ref(
- ref, np->backend_id, mfn, GNTMAP_readonly);
+ ref, np->xbdev->otherend_id, mfn, GNTMAP_readonly);
tx->gref = np->grant_tx_ref[id] = ref;
tx->offset = (unsigned long)skb->data & ~PAGE_MASK;
tx->size = skb->len;
static irqreturn_t netif_int(int irq, void *dev_id, struct pt_regs *ptregs)
{
struct net_device *dev = dev_id;
- struct net_private *np = netdev_priv(dev);
+ struct netfront_info *np = netdev_priv(dev);
unsigned long flags;
spin_lock_irqsave(&np->tx_lock, flags);
static int netif_poll(struct net_device *dev, int *pbudget)
{
- struct net_private *np = netdev_priv(dev);
+ struct netfront_info *np = netdev_priv(dev);
struct sk_buff *skb, *nskb;
netif_rx_response_t *rx;
NETIF_RING_IDX i, rp;
if(ref == GRANT_INVALID_REF) {
printk(KERN_WARNING "Bad rx grant reference %d "
"from dom %d.\n",
- ref, np->backend_id);
+ ref, np->xbdev->otherend_id);
np->rx->ring[MASK_NETIF_RX_IDX(np->rx->req_prod)].
req.id = rx->id;
wmb();
static int network_close(struct net_device *dev)
{
- struct net_private *np = netdev_priv(dev);
+ struct netfront_info *np = netdev_priv(dev);
np->user_state = UST_CLOSED;
netif_stop_queue(np->netdev);
return 0;
static struct net_device_stats *network_get_stats(struct net_device *dev)
{
- struct net_private *np = netdev_priv(dev);
+ struct netfront_info *np = netdev_priv(dev);
return &np->stats;
}
static void network_connect(struct net_device *dev)
{
- struct net_private *np;
+ struct netfront_info *np;
int i, requeue_idx;
netif_tx_request_t *tx;
struct sk_buff *skb;
tx->id = i;
gnttab_grant_foreign_access_ref(
- np->grant_tx_ref[i], np->backend_id,
+ np->grant_tx_ref[i], np->xbdev->otherend_id,
virt_to_mfn(np->tx_skbs[i]->data),
GNTMAP_readonly);
tx->gref = np->grant_tx_ref[i];
if ((unsigned long)np->rx_skbs[i] < __PAGE_OFFSET)
continue;
gnttab_grant_foreign_transfer_ref(
- np->grant_rx_ref[i], np->backend_id);
+ np->grant_rx_ref[i], np->xbdev->otherend_id);
np->rx->ring[requeue_idx].req.gref =
np->grant_rx_ref[i];
np->rx->ring[requeue_idx].req.id = i;
spin_unlock_irq(&np->tx_lock);
}
-static void show_device(struct net_private *np)
+static void show_device(struct netfront_info *np)
{
#ifdef DEBUG
if (np) {
#endif
}
-/*
- * Move the vif into connected state.
- * Sets the mac and event channel from the message.
- * Binds the irq to the event channel.
- */
-static void
-connect_device(struct net_private *np, unsigned int evtchn)
-{
- struct net_device *dev = np->netdev;
- memcpy(dev->dev_addr, np->mac, ETH_ALEN);
- np->evtchn = evtchn;
- network_connect(dev);
- np->irq = bind_evtchn_to_irqhandler(
- np->evtchn, netif_int, SA_SAMPLE_RANDOM, dev->name, dev);
- (void)send_fake_arp(dev);
- show_device(np);
-}
-
static void netif_uninit(struct net_device *dev)
{
- struct net_private *np = netdev_priv(dev);
+ struct netfront_info *np = netdev_priv(dev);
gnttab_free_grant_references(np->gref_tx_head);
gnttab_free_grant_references(np->gref_rx_head);
}
{
int i, err = 0;
struct net_device *netdev = NULL;
- struct net_private *np = NULL;
+ struct netfront_info *np = NULL;
- if ((netdev = alloc_etherdev(sizeof(struct net_private))) == NULL) {
+ if ((netdev = alloc_etherdev(sizeof(struct netfront_info))) == NULL) {
printk(KERN_WARNING "%s> alloc_etherdev failed.\n",
__FUNCTION__);
err = -ENOMEM;
goto exit;
}
-static int destroy_netdev(struct net_device *netdev)
-{
-#ifdef CONFIG_PROC_FS
- xennet_proc_delif(netdev);
-#endif
- unregister_netdev(netdev);
- return 0;
-}
-
/*
* We use this notifier to send out a fake ARP reply to reset switches and
* router ARP caches when an IP interface is brought up on a VIF.
return NOTIFY_DONE;
}
-static struct notifier_block notifier_inetdev = {
- .notifier_call = inetdev_notify,
- .next = NULL,
- .priority = 0
-};
-static struct xenbus_device_id netfront_ids[] = {
- { "vif" },
- { "" }
-};
+/* ** Close down ** */
-static void watch_for_status(struct xenbus_watch *watch,
- const char **vec, unsigned int len)
-{
-}
-static int setup_device(struct xenbus_device *dev, struct netfront_info *info)
+/**
+ * Handle the change of state of the backend to Closing. We must delete our
+ * device-layer structures now, to ensure that writes are flushed through to
+ * the backend. Once is this done, we can switch to Closed in
+ * acknowledgement.
+ */
+static void netfront_closing(struct xenbus_device *dev)
{
- int err;
- evtchn_op_t op = {
- .cmd = EVTCHNOP_alloc_unbound,
- .u.alloc_unbound.dom = DOMID_SELF,
- .u.alloc_unbound.remote_dom = info->backend_id };
+ struct netfront_info *info = dev->data;
- info->tx_ring_ref = GRANT_INVALID_REF;
- info->rx_ring_ref = GRANT_INVALID_REF;
- info->rx = NULL;
- info->tx = NULL;
- info->irq = 0;
+ DPRINTK("netfront_closing: %s removed\n", dev->nodename);
- info->tx = (netif_tx_interface_t *)__get_free_page(GFP_KERNEL);
- if (info->tx == 0) {
- err = -ENOMEM;
- xenbus_dev_error(dev, err, "allocating tx ring page");
- goto out;
- }
- info->rx = (netif_rx_interface_t *)__get_free_page(GFP_KERNEL);
- if (info->rx == 0) {
- err = -ENOMEM;
- xenbus_dev_error(dev, err, "allocating rx ring page");
- goto out;
- }
- memset(info->tx, 0, PAGE_SIZE);
- memset(info->rx, 0, PAGE_SIZE);
- info->backend_state = BEST_DISCONNECTED;
+ close_netdev(info);
- err = gnttab_grant_foreign_access(info->backend_id,
- virt_to_mfn(info->tx), 0);
- if (err < 0) {
- xenbus_dev_error(dev, err, "granting access to tx ring page");
- goto out;
- }
- info->tx_ring_ref = err;
+ xenbus_switch_state(dev, NULL, XenbusStateClosed);
+}
- err = gnttab_grant_foreign_access(info->backend_id,
- virt_to_mfn(info->rx), 0);
- if (err < 0) {
- xenbus_dev_error(dev, err, "granting access to rx ring page");
- goto out;
- }
- info->rx_ring_ref = err;
- err = HYPERVISOR_event_channel_op(&op);
- if (err) {
- xenbus_dev_error(dev, err, "allocating event channel");
- goto out;
- }
+static int netfront_remove(struct xenbus_device *dev)
+{
+ struct netfront_info *info = dev->data;
- connect_device(info, op.u.alloc_unbound.port);
+ DPRINTK("%s\n", dev->nodename);
+
+ netif_free(info);
+ kfree(info);
return 0;
+}
- out:
- netif_free(info);
- return err;
+
+static void netif_free(struct netfront_info *info)
+{
+ netif_disconnect_backend(info);
+ close_netdev(info);
}
-static void end_access(int ref, void *page)
+
+static void close_netdev(struct netfront_info *info)
{
- if (ref != GRANT_INVALID_REF)
- gnttab_end_foreign_access(ref, 0, (unsigned long)page);
+ if (info->netdev) {
+#ifdef CONFIG_PROC_FS
+ xennet_proc_delif(info->netdev);
+#endif
+ unregister_netdev(info->netdev);
+ info->netdev = NULL;
+ }
}
-static void netif_free(struct netfront_info *info)
+
+static void netif_disconnect_backend(struct netfront_info *info)
{
+ /* Stop old i/f to prevent errors whilst we rebuild the state. */
+ spin_lock_irq(&info->tx_lock);
+ spin_lock(&info->rx_lock);
+ netif_stop_queue(info->netdev);
+ /* info->backend_state = BEST_DISCONNECTED; */
+ spin_unlock(&info->rx_lock);
+ spin_unlock_irq(&info->tx_lock);
+
end_access(info->tx_ring_ref, info->tx);
end_access(info->rx_ring_ref, info->rx);
info->tx_ring_ref = GRANT_INVALID_REF;
info->evtchn = info->irq = 0;
}
-/* Stop network device and free tx/rx queues and irq. */
-static void shutdown_device(struct net_private *np)
-{
- /* Stop old i/f to prevent errors whilst we rebuild the state. */
- spin_lock_irq(&np->tx_lock);
- spin_lock(&np->rx_lock);
- netif_stop_queue(np->netdev);
- /* np->backend_state = BEST_DISCONNECTED; */
- spin_unlock(&np->rx_lock);
- spin_unlock_irq(&np->tx_lock);
-
- /* Free resources. */
- netif_free(np);
-}
-
-/* Common code used when first setting up, and when resuming. */
-static int talk_to_backend(struct xenbus_device *dev,
- struct netfront_info *info)
-{
- char *backend, *mac, *e, *s;
- const char *message;
- struct xenbus_transaction *xbt;
- int err, i;
-
- backend = NULL;
- err = xenbus_gather(NULL, dev->nodename,
- "backend-id", "%i", &info->backend_id,
- "backend", NULL, &backend,
- NULL);
- if (XENBUS_EXIST_ERR(err))
- goto out;
- if (backend && strlen(backend) == 0) {
- err = -ENOENT;
- goto out;
- }
- if (err < 0) {
- xenbus_dev_error(dev, err, "reading %s/backend or backend-id",
- dev->nodename);
- goto out;
- }
-
- mac = xenbus_read(NULL, dev->nodename, "mac", NULL);
- if (IS_ERR(mac)) {
- err = PTR_ERR(mac);
- xenbus_dev_error(dev, err, "reading %s/mac",
- dev->nodename);
- goto out;
- }
- s = mac;
- for (i = 0; i < ETH_ALEN; i++) {
- info->mac[i] = simple_strtoul(s, &e, 16);
- if (s == e || (e[0] != ':' && e[0] != 0)) {
- kfree(mac);
- err = -ENOENT;
- xenbus_dev_error(dev, err, "parsing %s/mac",
- dev->nodename);
- goto out;
- }
- s = &e[1];
- }
- kfree(mac);
-
- /* Create shared ring, alloc event channel. */
- err = setup_device(dev, info);
- if (err) {
- xenbus_dev_error(dev, err, "setting up ring");
- goto out;
- }
-
-again:
- xbt = xenbus_transaction_start();
- if (IS_ERR(xbt)) {
- xenbus_dev_error(dev, err, "starting transaction");
- goto destroy_ring;
- }
-
- err = xenbus_printf(xbt, dev->nodename, "tx-ring-ref","%u",
- info->tx_ring_ref);
- if (err) {
- message = "writing tx ring-ref";
- goto abort_transaction;
- }
- err = xenbus_printf(xbt, dev->nodename, "rx-ring-ref","%u",
- info->rx_ring_ref);
- if (err) {
- message = "writing rx ring-ref";
- goto abort_transaction;
- }
- err = xenbus_printf(xbt, dev->nodename,
- "event-channel", "%u", info->evtchn);
- if (err) {
- message = "writing event-channel";
- goto abort_transaction;
- }
-
- err = xenbus_transaction_end(xbt, 0);
- if (err) {
- if (err == -EAGAIN)
- goto again;
- xenbus_dev_error(dev, err, "completing transaction");
- goto destroy_ring;
- }
-
- info->watch.node = backend;
- info->watch.callback = watch_for_status;
- err = register_xenbus_watch(&info->watch);
- if (err) {
- message = "registering watch on backend";
- goto destroy_ring;
- }
-
- info->backend = backend;
-
- return 0;
- abort_transaction:
- xenbus_transaction_end(xbt, 1);
- xenbus_dev_error(dev, err, "%s", message);
- destroy_ring:
- shutdown_device(info);
- out:
- kfree(backend);
- return err;
-}
-
-/*
- * Setup supplies the backend dir, virtual device.
- * We place an event channel and shared frame entries.
- * We watch backend to wait if it's ok.
- */
-static int netfront_probe(struct xenbus_device *dev,
- const struct xenbus_device_id *id)
+static void end_access(int ref, void *page)
{
- int err;
- struct net_device *netdev;
- struct netfront_info *info;
- unsigned int handle;
-
- err = xenbus_scanf(NULL, dev->nodename, "handle", "%u", &handle);
- if (XENBUS_EXIST_ERR(err))
- return err;
- if (err < 0) {
- xenbus_dev_error(dev, err, "reading handle");
- return err;
- }
-
- err = create_netdev(handle, dev, &netdev);
- if (err) {
- xenbus_dev_error(dev, err, "creating netdev");
- return err;
- }
-
- info = netdev_priv(netdev);
- dev->data = info;
-
- err = talk_to_backend(dev, info);
- if (err) {
- destroy_netdev(netdev);
- kfree(netdev);
- dev->data = NULL;
- return err;
- }
-
- return 0;
+ if (ref != GRANT_INVALID_REF)
+ gnttab_end_foreign_access(ref, 0, (unsigned long)page);
}
-static int netfront_remove(struct xenbus_device *dev)
-{
- struct netfront_info *info = dev->data;
- if (info->backend)
- unregister_xenbus_watch(&info->watch);
+/* ** Driver registration ** */
- netif_free(info);
- kfree(info->backend);
- kfree(info);
-
- return 0;
-}
-
-static int netfront_suspend(struct xenbus_device *dev)
-{
- struct netfront_info *info = dev->data;
- unregister_xenbus_watch(&info->watch);
- kfree(info->backend);
- info->backend = NULL;
- return 0;
-}
+static struct xenbus_device_id netfront_ids[] = {
+ { "vif" },
+ { "" }
+};
-static int netfront_resume(struct xenbus_device *dev)
-{
- struct netfront_info *info = dev->data;
- netif_free(info);
- return talk_to_backend(dev, info);
-}
static struct xenbus_driver netfront = {
.name = "vif",
.probe = netfront_probe,
.remove = netfront_remove,
.resume = netfront_resume,
- .suspend = netfront_suspend,
+ .otherend_changed = backend_changed,
};
-static void __init init_net_xenbus(void)
-{
- xenbus_register_driver(&netfront);
-}
+
+static struct notifier_block notifier_inetdev = {
+ .notifier_call = inetdev_notify,
+ .next = NULL,
+ .priority = 0
+};
static int __init netif_init(void)
{
(void)register_inetaddr_notifier(¬ifier_inetdev);
- init_net_xenbus();
-
- return err;
+ return xenbus_register_frontend(&netfront);
}
+module_init(netif_init);
+
static void netif_exit(void)
{
+ unregister_inetaddr_notifier(¬ifier_inetdev);
+
+ return xenbus_unregister_driver(&netfront);
}
+module_exit(netif_exit);
+
+MODULE_LICENSE("BSD");
+
+
+/* ** /proc **/
+
#ifdef CONFIG_PROC_FS
{
struct net_device *dev =
(struct net_device *)((unsigned long)data & ~3UL);
- struct net_private *np = netdev_priv(dev);
+ struct netfront_info *np = netdev_priv(dev);
int len = 0, which_target = (long)data & 3;
switch (which_target)
{
struct net_device *dev =
(struct net_device *)((unsigned long)data & ~3UL);
- struct net_private *np = netdev_priv(dev);
+ struct netfront_info *np = netdev_priv(dev);
int which_target = (long)data & 3;
char string[64];
long target;
#endif
-module_init(netif_init);
-module_exit(netif_exit);
/*
* Local variables:
obj-y += xenbus.o
xenbus-objs =
+xenbus-objs += xenbus_client.o
xenbus-objs += xenbus_comms.o
xenbus-objs += xenbus_xs.o
xenbus-objs += xenbus_probe.o
--- /dev/null
+/******************************************************************************
+ * Client-facing interface for the Xenbus driver. In other words, the
+ * interface between the Xenbus and the device-specific code, be it the
+ * frontend or the backend of that driver.
+ *
+ * Copyright (C) 2005 XenSource Ltd
+ *
+ * This file may be distributed separately from the Linux kernel, or
+ * incorporated into other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+
+#if 0
+#define DPRINTK(fmt, args...) \
+ printk("xenbus_client (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args)
+#else
+#define DPRINTK(fmt, args...) ((void)0)
+#endif
+
+
+#include <asm-xen/evtchn.h>
+#include <asm-xen/gnttab.h>
+#include <asm-xen/xenbus.h>
+
+
+int xenbus_watch_path(struct xenbus_device *dev, const char *path,
+ struct xenbus_watch *watch,
+ void (*callback)(struct xenbus_watch *,
+ const char **, unsigned int))
+{
+ int err;
+
+ watch->node = path;
+ watch->callback = callback;
+
+ err = register_xenbus_watch(watch);
+
+ if (err) {
+ watch->node = NULL;
+ watch->callback = NULL;
+ xenbus_dev_fatal(dev, err, "adding watch on %s", path);
+ }
+
+ return err;
+}
+EXPORT_SYMBOL(xenbus_watch_path);
+
+
+int xenbus_watch_path2(struct xenbus_device *dev, const char *path,
+ const char *path2, struct xenbus_watch *watch,
+ void (*callback)(struct xenbus_watch *,
+ const char **, unsigned int))
+{
+ int err;
+ char *state =
+ kmalloc(strlen(path) + 1 + strlen(path2) + 1, GFP_KERNEL);
+ if (!state) {
+ xenbus_dev_fatal(dev, -ENOMEM, "allocating path for watch");
+ return -ENOMEM;
+ }
+ strcpy(state, path);
+ strcat(state, "/");
+ strcat(state, path2);
+
+ err = xenbus_watch_path(dev, state, watch, callback);
+
+ if (err) {
+ kfree(state);
+ }
+ return err;
+}
+EXPORT_SYMBOL(xenbus_watch_path2);
+
+
+int xenbus_switch_state(struct xenbus_device *dev,
+ struct xenbus_transaction *xbt,
+ XenbusState state)
+{
+ /* We check whether the state is currently set to the given value, and
+ if not, then the state is set. We don't want to unconditionally
+ write the given state, because we don't want to fire watches
+ unnecessarily.
+ */
+
+ int current_state;
+
+ int err = xenbus_scanf(xbt, dev->nodename, "state", "%d",
+ ¤t_state);
+ if (err == 1 && (XenbusState)current_state == state)
+ return 0;
+
+ err = xenbus_printf(xbt, dev->nodename, "state", "%d", state);
+ if (err) {
+ xenbus_dev_fatal(dev, err, "writing new state");
+ return err;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(xenbus_switch_state);
+
+
+/**
+ * Return the path to the error node for the given device, or NULL on failure.
+ * If the value returned is non-NULL, then it is the caller's to kfree.
+ */
+static char *error_path(struct xenbus_device *dev)
+{
+ char *path_buffer = kmalloc(strlen("error/") + strlen(dev->nodename) +
+ 1, GFP_KERNEL);
+ if (path_buffer == NULL) {
+ return NULL;
+ }
+
+ strcpy(path_buffer, "error/");
+ strcpy(path_buffer + strlen("error/"), dev->nodename);
+
+ return path_buffer;
+}
+
+
+void _dev_error(struct xenbus_device *dev, int err, const char *fmt,
+ va_list ap)
+{
+ int ret;
+ unsigned int len;
+ char *printf_buffer = NULL, *path_buffer = NULL;
+
+#define PRINTF_BUFFER_SIZE 4096
+ printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL);
+ if (printf_buffer == NULL)
+ goto fail;
+
+ len = sprintf(printf_buffer, "%i ", -err);
+ ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap);
+
+ BUG_ON(len + ret > PRINTF_BUFFER_SIZE-1);
+ dev->has_error = 1;
+
+ path_buffer = error_path(dev);
+
+ if (path_buffer == NULL) {
+ printk("xenbus: failed to write error node for %s (%s)\n",
+ dev->nodename, printf_buffer);
+ goto fail;
+ }
+
+ if (xenbus_write(NULL, path_buffer, "error", printf_buffer) != 0) {
+ printk("xenbus: failed to write error node for %s (%s)\n",
+ dev->nodename, printf_buffer);
+ goto fail;
+ }
+
+fail:
+ if (printf_buffer)
+ kfree(printf_buffer);
+ if (path_buffer)
+ kfree(path_buffer);
+}
+
+
+void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt,
+ ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _dev_error(dev, err, fmt, ap);
+ va_end(ap);
+}
+EXPORT_SYMBOL(xenbus_dev_error);
+
+
+void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt,
+ ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _dev_error(dev, err, fmt, ap);
+ va_end(ap);
+
+ xenbus_switch_state(dev, NULL, XenbusStateClosing);
+}
+EXPORT_SYMBOL(xenbus_dev_fatal);
+
+
+int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn)
+{
+ int err = gnttab_grant_foreign_access(dev->otherend_id, ring_mfn, 0);
+ if (err < 0)
+ xenbus_dev_fatal(dev, err, "granting access to ring page");
+ return err;
+}
+EXPORT_SYMBOL(xenbus_grant_ring);
+
+
+int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port)
+{
+ evtchn_op_t op = {
+ .cmd = EVTCHNOP_alloc_unbound,
+ .u.alloc_unbound.dom = DOMID_SELF,
+ .u.alloc_unbound.remote_dom = dev->otherend_id };
+
+ int err = HYPERVISOR_event_channel_op(&op);
+ if (err)
+ xenbus_dev_fatal(dev, err, "allocating event channel");
+ else
+ *port = op.u.alloc_unbound.port;
+ return err;
+}
+EXPORT_SYMBOL(xenbus_alloc_evtchn);
+
+
+XenbusState xenbus_read_driver_state(const char *path)
+{
+ XenbusState result;
+
+ int err = xenbus_gather(NULL, path, "state", "%d", &result, NULL);
+ if (err)
+ result = XenbusStateClosed;
+
+ return result;
+}
+EXPORT_SYMBOL(xenbus_read_driver_state);
+
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * indent-tabs-mode: t
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ */
*
* Copyright (C) 2005 Rusty Russell, IBM Corporation
* Copyright (C) 2005 Mike Wray, Hewlett-Packard
+ * Copyright (C) 2005 XenSource Ltd
*
* This file may be distributed separately from the Linux kernel, or
* incorporated into other software packages, subject to the following license:
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
-#define DEBUG
+
+#if 0
+#define DPRINTK(fmt, args...) \
+ printk("xenbus_probe (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args)
+#else
+#define DPRINTK(fmt, args...) ((void)0)
+#endif
#include <linux/kernel.h>
#include <linux/err.h>
struct device dev;
};
+
/* device/<type>/<id> => <type>-<id> */
static int frontend_bus_id(char bus_id[BUS_ID_SIZE], const char *nodename)
{
return 0;
}
+
+static int read_otherend_details(struct xenbus_device *xendev,
+ char *id_node, char *path_node)
+{
+ int err = xenbus_gather(NULL, xendev->nodename,
+ id_node, "%i", &xendev->otherend_id,
+ path_node, NULL, &xendev->otherend,
+ NULL);
+ if (err) {
+ xenbus_dev_fatal(xendev, err,
+ "reading other end details from %s",
+ xendev->nodename);
+ return err;
+ }
+ if (strlen(xendev->otherend) == 0 ||
+ !xenbus_exists(NULL, xendev->otherend, "")) {
+ xenbus_dev_fatal(xendev, -ENOENT, "missing other end from %s",
+ xendev->nodename);
+ kfree(xendev->otherend);
+ xendev->otherend = NULL;
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+
+static int read_backend_details(struct xenbus_device *xendev)
+{
+ return read_otherend_details(xendev, "backend-id", "backend");
+}
+
+
+static int read_frontend_details(struct xenbus_device *xendev)
+{
+ return read_otherend_details(xendev, "frontend-id", "frontend");
+}
+
+
+static void free_otherend_details(struct xenbus_device *dev)
+{
+ kfree(dev->otherend);
+ dev->otherend = NULL;
+}
+
+
+static void free_otherend_watch(struct xenbus_device *dev)
+{
+ if (dev->otherend_watch.node) {
+ unregister_xenbus_watch(&dev->otherend_watch);
+ kfree(dev->otherend_watch.node);
+ dev->otherend_watch.node = NULL;
+ }
+}
+
+
/* Bus type for frontend drivers. */
static int xenbus_probe_frontend(const char *type, const char *name);
static struct xen_bus_type xenbus_frontend = {
int i = 0;
int length = 0;
+ DPRINTK("");
+
if (dev == NULL)
return -ENODEV;
},
};
+
+static void otherend_changed(struct xenbus_watch *watch,
+ const char **vec, unsigned int len)
+{
+ struct xenbus_device *dev =
+ container_of(watch, struct xenbus_device, otherend_watch);
+ struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver);
+
+ /* Protect us against watches firing on old details when the otherend
+ details change, say immediately after a resume. */
+ if (!dev->otherend ||
+ strncmp(dev->otherend, vec[XS_WATCH_PATH],
+ strlen(dev->otherend))) {
+ DPRINTK("Ignoring watch at %s", vec[XS_WATCH_PATH]);
+ return;
+ }
+
+ XenbusState state = xenbus_read_driver_state(dev->otherend);
+
+ DPRINTK("state is %d, %s, %s", state, dev->otherend_watch.node, vec[XS_WATCH_PATH]);
+
+ drv->otherend_changed(dev, state);
+}
+
+
+static int talk_to_otherend(struct xenbus_device *dev)
+{
+ struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver);
+ int err;
+
+ free_otherend_watch(dev);
+ free_otherend_details(dev);
+
+ err = drv->read_otherend_details(dev);
+ if (err)
+ return err;
+
+ return xenbus_watch_path2(dev, dev->otherend, "state",
+ &dev->otherend_watch, otherend_changed);
+}
+
+
static int xenbus_dev_probe(struct device *_dev)
{
struct xenbus_device *dev = to_xenbus_device(_dev);
struct xenbus_driver *drv = to_xenbus_driver(_dev->driver);
const struct xenbus_device_id *id;
+ int err;
- if (!drv->probe)
- return -ENODEV;
+ DPRINTK("");
+
+ err = talk_to_otherend(dev);
+ if (err) {
+ printk(KERN_WARNING
+ "xenbus_probe: talk_to_otherend on %s failed.\n",
+ dev->nodename);
+ return err;
+ }
+
+ if (!drv->probe) {
+ err = -ENODEV;
+ goto fail;
+ }
id = match_device(drv->ids, dev);
- if (!id)
- return -ENODEV;
+ if (!id) {
+ err = -ENODEV;
+ goto fail;
+ }
+
+ err = drv->probe(dev, id);
+ if (err)
+ goto fail;
- return drv->probe(dev, id);
+ return 0;
+fail:
+ xenbus_dev_error(dev, err, "xenbus_dev_probe on %s", dev->nodename);
+ xenbus_switch_state(dev, NULL, XenbusStateClosed);
+ return -ENODEV;
+
}
static int xenbus_dev_remove(struct device *_dev)
struct xenbus_device *dev = to_xenbus_device(_dev);
struct xenbus_driver *drv = to_xenbus_driver(_dev->driver);
- if (!drv->remove)
- return 0;
- return drv->remove(dev);
+ DPRINTK("");
+
+ free_otherend_watch(dev);
+ free_otherend_details(dev);
+
+ if (drv->remove)
+ drv->remove(dev);
+
+ xenbus_switch_state(dev, NULL, XenbusStateClosed);
+ return 0;
}
static int xenbus_register_driver_common(struct xenbus_driver *drv,
return ret;
}
-int xenbus_register_driver(struct xenbus_driver *drv)
+int xenbus_register_frontend(struct xenbus_driver *drv)
{
+ drv->read_otherend_details = read_backend_details;
+
return xenbus_register_driver_common(drv, &xenbus_frontend);
}
-EXPORT_SYMBOL(xenbus_register_driver);
+EXPORT_SYMBOL(xenbus_register_frontend);
int xenbus_register_backend(struct xenbus_driver *drv)
{
+ drv->read_otherend_details = read_frontend_details;
+
return xenbus_register_driver_common(drv, &xenbus_backend);
}
+EXPORT_SYMBOL(xenbus_register_backend);
void xenbus_unregister_driver(struct xenbus_driver *drv)
{
struct xb_find_info *info = data;
int len = strlen(info->nodename);
+ DPRINTK("%s", info->nodename);
+
if (!strncmp(xendev->nodename, info->nodename, len)) {
info->dev = xendev;
get_device(dev);
} while (info.dev);
}
-static void xenbus_release_device(struct device *dev)
+static void xenbus_dev_free(struct xenbus_device *xendev)
{
- if (dev) {
- struct xenbus_device *xendev = to_xenbus_device(dev);
+ kfree(xendev);
+}
- kfree(xendev);
+static void xenbus_dev_release(struct device *dev)
+{
+ if (dev) {
+ xenbus_dev_free(to_xenbus_device(dev));
}
}
}
DEVICE_ATTR(devtype, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_devtype, NULL);
+
static int xenbus_probe_node(struct xen_bus_type *bus,
const char *type,
const char *nodename)
{
+#define CHECK_FAIL \
+ do { \
+ if (err) \
+ goto fail; \
+ } \
+ while (0) \
+
+
int err;
struct xenbus_device *xendev;
- unsigned int stringlen;
+ size_t stringlen;
+ char *tmpstring;
+
+ XenbusState state = xenbus_read_driver_state(nodename);
+
+ if (state != XenbusStateInitialising) {
+ /* Device is not new, so ignore it. This can happen if a
+ device is going away after switching to Closed. */
+ return 0;
+ }
stringlen = strlen(nodename) + 1 + strlen(type) + 1;
xendev = kmalloc(sizeof(*xendev) + stringlen, GFP_KERNEL);
memset(xendev, 0, sizeof(*xendev));
/* Copy the strings into the extra space. */
- xendev->nodename = (char *)(xendev + 1);
- strcpy(xendev->nodename, nodename);
- xendev->devicetype = xendev->nodename + strlen(xendev->nodename) + 1;
- strcpy(xendev->devicetype, type);
+
+ tmpstring = (char *)(xendev + 1);
+ strcpy(tmpstring, nodename);
+ xendev->nodename = tmpstring;
+
+ tmpstring += strlen(tmpstring) + 1;
+ strcpy(tmpstring, type);
+ xendev->devicetype = tmpstring;
xendev->dev.parent = &bus->dev;
xendev->dev.bus = &bus->bus;
- xendev->dev.release = xenbus_release_device;
+ xendev->dev.release = xenbus_dev_release;
err = bus->get_bus_id(xendev->dev.bus_id, xendev->nodename);
- if (err) {
- kfree(xendev);
- return err;
- }
+ CHECK_FAIL;
/* Register with generic device framework. */
err = device_register(&xendev->dev);
- if (err) {
- printk("XENBUS: Registering %s device %s: error %i\n",
- bus->bus.name, xendev->dev.bus_id, err);
- kfree(xendev);
- } else {
- device_create_file(&xendev->dev, &dev_attr_nodename);
- device_create_file(&xendev->dev, &dev_attr_devtype);
- }
+ CHECK_FAIL;
+
+ device_create_file(&xendev->dev, &dev_attr_nodename);
+ device_create_file(&xendev->dev, &dev_attr_devtype);
+
+ return 0;
+
+#undef CHECK_FAIL
+
+fail:
+ xenbus_dev_free(xendev);
return err;
}
if (!nodename)
return -ENOMEM;
+ DPRINTK("%s", nodename);
+
err = xenbus_probe_node(&xenbus_frontend, type, nodename);
kfree(nodename);
return err;
if (!nodename)
return -ENOMEM;
+ DPRINTK("%s\n", nodename);
+
err = xenbus_probe_node(&xenbus_backend, type, nodename);
kfree(nodename);
return err;
char **dir;
unsigned int i, dir_n = 0;
+ DPRINTK("");
+
nodename = kasprintf("%s/%s/%s", xenbus_backend.root, type, domid);
if (!nodename)
return -ENOMEM;
static void frontend_changed(struct xenbus_watch *watch,
const char **vec, unsigned int len)
{
+ DPRINTK("");
+
dev_changed(vec[XS_WATCH_PATH], &xenbus_frontend);
}
static void backend_changed(struct xenbus_watch *watch,
const char **vec, unsigned int len)
{
+ DPRINTK("");
+
dev_changed(vec[XS_WATCH_PATH], &xenbus_backend);
}
struct xenbus_driver *drv;
struct xenbus_device *xdev;
+ DPRINTK("");
+
if (dev->driver == NULL)
return 0;
drv = to_xenbus_driver(dev->driver);
if (drv->suspend)
err = drv->suspend(xdev);
if (err)
- printk("xenbus: suspend %s failed: %i\n", dev->bus_id, err);
+ printk(KERN_WARNING
+ "xenbus: suspend %s failed: %i\n", dev->bus_id, err);
return 0;
}
static int resume_dev(struct device *dev, void *data)
{
- int err = 0;
+ int err;
struct xenbus_driver *drv;
struct xenbus_device *xdev;
+ DPRINTK("");
+
if (dev->driver == NULL)
return 0;
drv = to_xenbus_driver(dev->driver);
xdev = container_of(dev, struct xenbus_device, dev);
+
+ err = talk_to_otherend(xdev);
+ if (err) {
+ printk(KERN_WARNING
+ "xenbus: resume (talk_to_otherend) %s failed: %i\n",
+ dev->bus_id, err);
+ return err;
+ }
+
if (drv->resume)
err = drv->resume(xdev);
if (err)
- printk("xenbus: resume %s failed: %i\n", dev->bus_id, err);
- return 0;
+ printk(KERN_WARNING
+ "xenbus: resume %s failed: %i\n", dev->bus_id, err);
+ return err;
}
void xenbus_suspend(void)
{
+ DPRINTK("");
+
bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, suspend_dev);
bus_for_each_dev(&xenbus_backend.bus, NULL, NULL, suspend_dev);
xs_suspend();
}
+EXPORT_SYMBOL(xenbus_suspend);
void xenbus_resume(void)
{
bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, resume_dev);
bus_for_each_dev(&xenbus_backend.bus, NULL, NULL, resume_dev);
}
+EXPORT_SYMBOL(xenbus_resume);
/* A flag to determine if xenstored is 'ready' (i.e. has started) */
/* Notify others that xenstore is up */
notifier_call_chain(&xenstore_chain, 0, 0);
-
- return;
}
{
int err = 0, dom0;
- printk("xenbus_probe_init\n");
+ DPRINTK("");
if (xen_init() < 0) {
- printk("xen_init failed\n");
+ DPRINTK("failed");
return -ENODEV;
}
/* Initialize the interface to xenstore. */
err = xs_init();
if (err) {
- printk("XENBUS: Error initializing xenstore comms: %i\n", err);
+ printk(KERN_WARNING
+ "XENBUS: Error initializing xenstore comms: %i\n", err);
return err;
}
}
EXPORT_SYMBOL(xenbus_printf);
-/**
- * Return the path to the error node for the given device, or NULL on failure.
- * If the value returned is non-NULL, then it is the caller's to kfree.
- */
-static char *error_path(struct xenbus_device *dev)
-{
- char *path_buffer = kmalloc(strlen("error/") + strlen(dev->nodename) +
- 1, GFP_KERNEL);
- if (path_buffer == NULL) {
- return NULL;
- }
-
- strcpy(path_buffer, "error/");
- strcpy(path_buffer + strlen("error/"), dev->nodename);
-
- return path_buffer;
-}
-
-/* Report a (negative) errno into the store, with explanation. */
-void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, ...)
-{
- va_list ap;
- int ret;
- unsigned int len;
- char *printf_buffer = NULL, *path_buffer = NULL;
-
- printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL);
- if (printf_buffer == NULL)
- goto fail;
-
- len = sprintf(printf_buffer, "%i ", -err);
- va_start(ap, fmt);
- ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap);
- va_end(ap);
-
- BUG_ON(len + ret > PRINTF_BUFFER_SIZE-1);
- dev->has_error = 1;
-
- path_buffer = error_path(dev);
-
- if (path_buffer == NULL) {
- printk("xenbus: failed to write error node for %s (%s)\n",
- dev->nodename, printf_buffer);
- goto fail;
- }
-
- if (xenbus_write(NULL, path_buffer, "error", printf_buffer) != 0) {
- printk("xenbus: failed to write error node for %s (%s)\n",
- dev->nodename, printf_buffer);
- goto fail;
- }
-
-fail:
- kfree(printf_buffer);
- kfree(path_buffer);
-}
-EXPORT_SYMBOL(xenbus_dev_error);
-
-/* Clear any error. */
-void xenbus_dev_ok(struct xenbus_device *dev)
-{
- if (dev->has_error) {
- char *path_buffer = error_path(dev);
-
- if (path_buffer == NULL) {
- printk("xenbus: failed to clear error node for %s\n",
- dev->nodename);
- return;
- }
-
- if (xenbus_rm(NULL, path_buffer, "error") != 0)
- printk("xenbus: failed to clear error node for %s\n",
- dev->nodename);
- else
- dev->has_error = 0;
-
- kfree(path_buffer);
- }
-}
-EXPORT_SYMBOL(xenbus_dev_ok);
-
/* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
int xenbus_gather(struct xenbus_transaction *t, const char *dir, ...)
{
#include <asm/semaphore.h>
#include <asm-xen/xen-public/io/xs_wire.h>
+/* Register callback to watch this node. */
+struct xenbus_watch
+{
+ struct list_head list;
+
+ /* Path being watched. */
+ const char *node;
+
+ /* Callback (executed in a process context with no locks held). */
+ void (*callback)(struct xenbus_watch *,
+ const char **vec, unsigned int len);
+};
+
+
+/* The state of either end of the Xenbus, i.e. the current communication
+ status of initialisation across the bus. States here imply nothing about
+ the state of the connection between the driver and the kernel's device
+ layers. */
+typedef enum
+{
+ XenbusStateUnknown = 0,
+ XenbusStateInitialising = 1,
+ XenbusStateInitWait = 2, /* Finished early initialisation, but waiting
+ for information from the peer or hotplug
+ scripts. */
+ XenbusStateInitialised = 3, /* Initialised and waiting for a connection
+ from the peer. */
+ XenbusStateConnected = 4,
+ XenbusStateClosing = 5, /* The device is being closed due to an error
+ or an unplug event. */
+ XenbusStateClosed = 6
+
+} XenbusState;
+
+
/* A xenbus device. */
struct xenbus_device {
- char *devicetype;
- char *nodename;
+ const char *devicetype;
+ const char *nodename;
+ const char *otherend;
+ int otherend_id;
+ struct xenbus_watch otherend_watch;
struct device dev;
int has_error;
void *data;
const struct xenbus_device_id *ids;
int (*probe)(struct xenbus_device *dev,
const struct xenbus_device_id *id);
+ void (*otherend_changed)(struct xenbus_device *dev,
+ XenbusState backend_state);
int (*remove)(struct xenbus_device *dev);
int (*suspend)(struct xenbus_device *dev);
int (*resume)(struct xenbus_device *dev);
int (*hotplug)(struct xenbus_device *, char **, int, char *, int);
struct device_driver driver;
+ int (*read_otherend_details)(struct xenbus_device *dev);
};
static inline struct xenbus_driver *to_xenbus_driver(struct device_driver *drv)
return container_of(drv, struct xenbus_driver, driver);
}
-int xenbus_register_driver(struct xenbus_driver *drv);
+int xenbus_register_frontend(struct xenbus_driver *drv);
int xenbus_register_backend(struct xenbus_driver *drv);
void xenbus_unregister_driver(struct xenbus_driver *drv);
* sprintf-style type string, and pointer. Returns 0 or errno.*/
int xenbus_gather(struct xenbus_transaction *t, const char *dir, ...);
-/* Report a (negative) errno into the store, with explanation. */
-void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt,...);
-
-/* Clear any error. */
-void xenbus_dev_ok(struct xenbus_device *dev);
-
-/* Register callback to watch this node. */
-struct xenbus_watch
-{
- struct list_head list;
-
- /* Path being watched. */
- char *node;
-
- /* Callback (executed in a process context with no locks held). */
- void (*callback)(struct xenbus_watch *,
- const char **vec, unsigned int len);
-};
-
/* notifer routines for when the xenstore comes up */
int register_xenstore_notifier(struct notifier_block *nb);
void unregister_xenstore_notifier(struct notifier_block *nb);
#define XENBUS_EXIST_ERR(err) ((err) == -ENOENT || (err) == -ERANGE)
+
+/**
+ * Register a watch on the given path, using the given xenbus_watch structure
+ * for storage, and the given callback function as the callback. Return 0 on
+ * success, or -errno on error. On success, the given path will be saved as
+ * watch->node, and remains the caller's to free. On error, watch->node will
+ * be NULL, the device will switch to XenbusStateClosing, and the error will
+ * be saved in the store.
+ */
+int xenbus_watch_path(struct xenbus_device *dev, const char *path,
+ struct xenbus_watch *watch,
+ void (*callback)(struct xenbus_watch *,
+ const char **, unsigned int));
+
+
+/**
+ * Register a watch on the given path/path2, using the given xenbus_watch
+ * structure for storage, and the given callback function as the callback.
+ * Return 0 on success, or -errno on error. On success, the watched path
+ * (path/path2) will be saved as watch->node, and becomes the caller's to
+ * kfree(). On error, watch->node will be NULL, so the caller has nothing to
+ * free, the device will switch to XenbusStateClosing, and the error will be
+ * saved in the store.
+ */
+int xenbus_watch_path2(struct xenbus_device *dev, const char *path,
+ const char *path2, struct xenbus_watch *watch,
+ void (*callback)(struct xenbus_watch *,
+ const char **, unsigned int));
+
+
+/**
+ * Advertise in the store a change of the given driver to the given new_state.
+ * Perform the change inside the given transaction xbt. xbt may be NULL, in
+ * which case this is performed inside its own transaction. Return 0 on
+ * success, or -errno on error. On error, the device will switch to
+ * XenbusStateClosing, and the error will be saved in the store.
+ */
+int xenbus_switch_state(struct xenbus_device *dev,
+ struct xenbus_transaction *xbt,
+ XenbusState new_state);
+
+
+/**
+ * Grant access to the given ring_mfn to the peer of the given device. Return
+ * 0 on success, or -errno on error. On error, the device will switch to
+ * XenbusStateClosing, and the error will be saved in the store.
+ */
+int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn);
+
+
+/**
+ * Allocate an event channel for the given xenbus_device, assigning the newly
+ * created local port to *port. Return 0 on success, or -errno on error. On
+ * error, the device will switch to XenbusStateClosing, and the error will be
+ * saved in the store.
+ */
+int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port);
+
+
+/**
+ * Return the state of the driver rooted at the given store path, or
+ * XenbusStateClosed if no state can be read.
+ */
+XenbusState xenbus_read_driver_state(const char *path);
+
+
+/***
+ * Report the given negative errno into the store, along with the given
+ * formatted message.
+ */
+void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt,
+ ...);
+
+
+/***
+ * Equivalent to xenbus_dev_error(dev, err, fmt, args), followed by
+ * xenbus_switch_state(dev, NULL, XenbusStateClosing) to schedule an orderly
+ * closedown of this driver and its peer.
+ */
+void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt,
+ ...);
+
+
#endif /* _ASM_XEN_XENBUS_H */
/*
from xen.xend.XendClient import server
from xen.xend.XendError import XendError
from xen.xend.xenstore.xstransact import xstransact
+from xen.xend.server import DevController
import xen.xend.XendProtocol
print ("Cannot find backend path for device %s, %s." %
(deviceClass, device))
else:
- backend_error = xstransact.Read(
- backendPath.replace('backend/', 'error/backend/'),
- 'error')
+ frontend_state = xstransact.Read(frontendPath, 'state')
+ backend_state = xstransact.Read(backendPath, 'state')
- if backend_error:
- diagnose_device_error(backend_error)
+ print "Backend is in state %s." % stateString(backend_state)
+ print "Frontend is in state %s." % stateString(frontend_state)
+ check_for_error(True)
+ check_for_error(False)
-def diagnose_device_error(err):
- if re.search("2 reading .*/ring-ref and event-channel", err):
- print ("Backend is stuck waiting for frontend for device %s, %s." %
- (deviceClass, device))
- diagnose_stuck_frontend()
+ diagnose_hotplugging()
+
+
+def check_for_error(backend):
+ if backend:
+ path = backendPath.replace('backend/', 'error/backend/')
else:
- print ("Device %s, %s shows error %s." %
- (deviceClass, device, err))
+ path = frontendPath.replace('device/', 'error/device/')
+
+ err = xstransact.Read(path, 'error')
+
+ if err:
+ print ("%s for device %s, %s shows error %s." %
+ (backend and 'Backend' or 'Frontend', deviceClass, device,
+ err))
-def diagnose_stuck_frontend():
- if deviceClass == "vbd":
+def diagnose_hotplugging():
+ if deviceClass == 'vbd':
phy = xstransact.Read(backendPath, 'physical-device')
if phy:
- print ("Device %s, %s hotplugging has completed successfully." %
+ print ('Device %s, %s hotplugging has completed successfully, '
+ 'and is connected to physical device %s.' %
+ (deviceClass, device, phy))
+ else:
+ print ('Device %s, %s hotplugging failed.' %
(deviceClass, device))
+ elif deviceClass == 'vif':
+ handle = xstransact.Read(backendPath, 'handle')
+
+ if handle:
+ print ('Device %s, %s hotplugging has completed successfully, '
+ 'and is using handle %s.' %
+ (deviceClass, device, handle))
else:
- print ("Device %s, %s hotplugging failed." %
+ print ('Device %s, %s hotplugging failed.' %
(deviceClass, device))
+def stateString(state):
+ return state and DevController.xenbusState[int(state)] or '<None>'
+
+
def main(argv = None):
if argv is None:
argv = sys.argv
from xen.xend.xenstore.xstransact import xstransact
from xen.xend.xenstore.xswatch import xswatch
-DEVICE_CREATE_TIMEOUT = 120
+DEVICE_CREATE_TIMEOUT = 5
HOTPLUG_STATUS_NODE = "hotplug-status"
HOTPLUG_STATUS_ERROR = "error"
+xenbusState = {
+ 'Unknown' : 0,
+ 'Initialising' : 1,
+ 'InitWait' : 2,
+ 'Initialised' : 3,
+ 'Connected' : 4,
+ 'Closing' : 5,
+ 'Closed' : 6,
+ }
+
+xenbusState.update(dict(zip(xenbusState.values(), xenbusState.keys())))
+
+
class DevController:
"""Abstract base class for a device controller. Device controllers create
appropriate entries in the store to trigger the creation, reconfiguration,
frontpath = self.frontendPath(devid)
backpath = xstransact.Read(frontpath, "backend")
- xstransact.Remove(frontpath)
-
if backpath:
- xstransact.Remove(backpath)
+ xstransact.Write(backpath, 'state', str(xenbusState['Closing']))
else:
raise VmError("Device %s not connected" % devid)
frontpath = self.frontendPath(devid)
backpath = self.backendPath(backdom, devid)
- xstransact.Remove(backpath, HOTPLUG_STATUS_NODE)
-
frontDetails.update({
'backend' : backpath,
- 'backend-id' : "%i" % backdom.getDomid()
+ 'backend-id' : "%i" % backdom.getDomid(),
+ 'state' : str(xenbusState['Initialising'])
})
backDetails.update({
'domain' : self.vm.getName(),
'frontend' : frontpath,
- 'frontend-id' : "%i" % self.vm.getDomid()
+ 'frontend-id' : "%i" % self.vm.getDomid(),
+ 'state' : str(xenbusState['Initialising'])
})
log.debug('DevController: writing %s to %s.', str(frontDetails),
log.debug('DevController: writing %s to %s.', str(backDetails),
backpath)
- xstransact.Write(frontpath, frontDetails)
- xstransact.Write(backpath, backDetails)
+ while True:
+ t = xstransact()
+ try:
+ t.remove2(backpath, HOTPLUG_STATUS_NODE)
+
+ t.write2(frontpath, frontDetails)
+ t.write2(backpath, backDetails)
+
+ if t.commit():
+ return
+ except:
+ t.abort()
+ raise
+
def waitForBackend(self,devid):
ev = Event()
class xstransact:
- def __init__(self, path):
+ def __init__(self, path = ""):
assert path is not None
self.in_transaction = False # Set this temporarily -- if this
return rc
def _read(self, key):
- path = "%s/%s" % (self.path, key)
+ path = self.prependPath(key)
try:
return xshandle().read(self.transaction, path)
except RuntimeError, ex:
return ret
def _write(self, key, data):
- path = "%s/%s" % (self.path, key)
+ path = self.prependPath(key)
try:
xshandle().write(self.transaction, path, data)
except RuntimeError, ex:
raise TypeError
def _remove(self, key):
- path = "%s/%s" % (self.path, key)
+ path = self.prependPath(key)
return xshandle().rm(self.transaction, path)
def remove(self, *args):
self._remove(key)
def _list(self, key):
- path = "%s/%s" % (self.path, key)
+ path = self.prependPath(key)
l = xshandle().ls(self.transaction, path)
if l:
return map(lambda x: key + "/" + x, l)
self._write(key, fmt % val)
+ def remove2(self, middlePath, *args):
+ self.callRebased(middlePath, self.remove, *args)
+
+
+ def write2(self, middlePath, *args):
+ self.callRebased(middlePath, self.write, *args)
+
+
+ def callRebased(self, middlePath, func, *args):
+ oldpath = self.path
+ self.path = self.prependPath(middlePath)
+ try:
+ func(*args)
+ finally:
+ self.path = oldpath
+
+
+ def prependPath(self, key):
+ if self.path:
+ return self.path + '/' + key
+ else:
+ return key
+
+
def Read(cls, path, *args):
"""If only one argument is given (path), return the value stored at
that path. If two arguments are given, treat the second argument as a